<commit_before>/**
 * @file osx/drivenotifyosx.cpp
 * @brief Mega SDK various utilities and helper classes
 *
 * (c) 2013-2021 by Mega Limited, Auckland, New Zealand
 *
 * This file is part of the MEGA SDK - Client Access Engine.
 *
 * Applications using the MEGA API must present a valid application key
 * and comply with the the rules set forth in the Terms of Service.
 *
 * The MEGA SDK 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.
 *
 * @copyright Simplified (2-clause) BSD License.
 *
 * You should have received a copy of the license along with this
 * program.
 */

#ifdef USE_DRIVE_NOTIFICATIONS

#include "mega/drivenotify.h"

#include <sys/param.h>

namespace mega {

// CFArrayRef objects need to be constructed from const C-style array.
// We watch for changes in the Volume Path Key.
static const CFStringRef watchArray[1] = {kDADiskDescriptionVolumePathKey};

// Copied from Disk Arbitration Framework documentation. In the `else` below they write:
// "// something is *really* wrong"
// so we throw an exception.
static std::string toString(CFURLRef fspath)
{
    char buf[MAXPATHLEN];
    if (CFURLGetFileSystemRepresentation(fspath, false, reinterpret_cast<UInt8*>(buf), sizeof(buf)))
    {
        return buf;
    }

    throw std::runtime_error("Unable to resolve path from filesystem URL");
}

// See note here regarding callback registration/unregistration:
// https://developer.apple.com/library/archive/documentation/DriversKernelHardware/Conceptual/DiskArbitrationProgGuide/ArbitrationBasics/ArbitrationBasics.html#//apple_ref/doc/uid/TP40009310-CH2-SW9
// MediaTypeCallbacks is an abstract base class which uses the Non-Virtual Interface Pattern
// to implement callbacks, which are static member functions, where the `void* context` is a pointer
// to a concrete derived class. Per the note above, we are registering the same callback (i.e., pointer to static method)
// with a different context (i.e., pointer to derived class cast to void*)

void MediaTypeCallbacks::registerCallbacks(DASessionRef session)
{
    DARegisterDiskAppearedCallback(
        session,
        matchingDict(),
        &MediaTypeCallbacks::onDiskAppeared,
        this);

    DARegisterDiskDisappearedCallback(
        session,
        matchingDict(),
        &MediaTypeCallbacks::onDiskDisappeared,
        this);

    registerAdditionalCallbacks(session);
}

void MediaTypeCallbacks::unregisterCallbacks(DASessionRef session)
{
    DAUnregisterCallback(session, reinterpret_cast<void*>(&MediaTypeCallbacks::onDiskAppeared), this);
    DAUnregisterCallback(session, reinterpret_cast<void*>(&MediaTypeCallbacks::onDiskDisappeared), this);

    DAUnregisterCallback(session, reinterpret_cast<void*>(&MediaTypeCallbacks::onDiskDescriptionChanged), this);

    DAUnregisterApprovalCallback(session, reinterpret_cast<void*>(&MediaTypeCallbacks::onUnmountApproval), this);
}

void MediaTypeCallbacks::addDrive(CFURLRef path, bool connected)
{
    DriveInfo di;
    di.mountPoint = toString(path);
    di.connected = connected;
    mParent.add(std::move(di));
}

void MediaTypeCallbacks::onDiskAppeared(DADiskRef disk, void* context)
{
    MediaTypeCallbacks& self = castSelf(context);

    UniqueCFRef<CFDictionaryRef> diskDescription = description(disk);

    if (!self.shouldNotify(diskDescription)) return;

    CFURLRef path = volumePath(diskDescription);

    if (path)
    {
        self.addDrive(path, true);
    }
    else
    {
        self.handleNoPathAppeared(diskDescription);
    }
}

void MediaTypeCallbacks::onDiskDisappeared(DADiskRef disk, void* context)
{
    MediaTypeCallbacks& self = castSelf(context);

    UniqueCFRef<CFDictionaryRef> diskDescription = description(disk);

    if (!self.shouldNotify(diskDescription)) return;

    CFURLRef path = volumePath(diskDescription);

    if (path) self.addDrive(path, false);

    self.processDisappeared(diskDescription);
}

DADissenterRef MediaTypeCallbacks::onUnmountApproval(DADiskRef disk, void* context)
{
    MediaTypeCallbacks::onDiskDisappeared(disk, context);
    return nullptr;
}

PhysicalMediaCallbacks::PhysicalMediaCallbacks(DriveNotifyOsx& parent)
    : MediaTypeCallbacks(parent),
     mMatchingDict([]{
        // Constructing with immediately invoked lambda expression to allow storage of immutable ref
        CFMutableDictionaryRef matchingDict = CFDictionaryCreateMutable(
            kCFAllocatorDefault,
            3,
            &kCFTypeDictionaryKeyCallBacks,
            &kCFTypeDictionaryValueCallBacks);

        // Do not match Whole Media, else we get multiple notifications for the same drive
        CFDictionaryAddValue(matchingDict, kDADiskDescriptionMediaWholeKey, kCFBooleanFalse);

        // Match removable/ejectable media
        CFDictionaryAddValue(matchingDict, kDADiskDescriptionMediaRemovableKey, kCFBooleanTrue);
        CFDictionaryAddValue(matchingDict, kDADiskDescriptionMediaEjectableKey, kCFBooleanTrue);

        return matchingDict;
    }()),
    mKeysToMonitor(CFArrayCreate(
        kCFAllocatorDefault,
        reinterpret_cast<const void**>(&watchArray),
        1,
        &kCFTypeArrayCallBacks))
{}

bool PhysicalMediaCallbacks::shouldNotify(CFDictionaryRef diskDescription) const noexcept
{
    auto deviceProtocol = reinterpret_cast<CFStringRef>(
        CFDictionaryGetValue(diskDescription, kDADiskDescriptionDeviceProtocolKey));

    return CFStringCompare(deviceProtocol, CFSTR(kIOPropertyPhysicalInterconnectTypeVirtual), 0) != kCFCompareEqualTo;
}

void PhysicalMediaCallbacks::registerAdditionalCallbacks(DASessionRef session)
{
    DARegisterDiskDescriptionChangedCallback(
        session,
        matchingDict(),
        mKeysToMonitor,
        &MediaTypeCallbacks::onDiskDescriptionChanged,
        this);

    DARegisterDiskUnmountApprovalCallback(
        session,
        matchingDict(),
        &MediaTypeCallbacks::onUnmountApproval,
        this);
}

void PhysicalMediaCallbacks::handleNoPathAppeared(CFDictionaryRef diskDescription)
{
    CFUUIDRef uuid = volumeUUID(diskDescription);
    if (uuid) mDisksPendingPath.insert(CFUUIDGetUUIDBytes(uuid));
}

void PhysicalMediaCallbacks::processDisappeared(CFDictionaryRef diskDescription)
{
    CFUUIDRef uuid = volumeUUID(diskDescription);
    if (uuid) mDisksPendingPath.erase(CFUUIDGetUUIDBytes(uuid));
}

// The actual onDiskDescriptionChanged callback allows for significantly greater generality than our use, which is
// basically just to try again performing the onDiskAppeared logic with slight modification
void PhysicalMediaCallbacks::onDiskDescriptionChangedImpl(DADiskRef disk, CFArrayRef matchingKeys, void* context)
{
    auto diskDescription = UniqueCFRef<CFDictionaryRef>(MediaTypeCallbacks::description(disk));

    CFURLRef path = volumePath(diskDescription);
    if (!path) return;

    CFUUIDRef uuid = volumeUUID(diskDescription);
    if (!uuid) return;

    auto findItr = mDisksPendingPath.find(CFUUIDGetUUIDBytes(uuid));
    if (findItr == mDisksPendingPath.end()) return;

    addDrive(path, true);

    mDisksPendingPath.erase(findItr);
}

NetworkDriveCallbacks::NetworkDriveCallbacks(DriveNotifyOsx& parent)
    : MediaTypeCallbacks(parent),
    mMatchingDict([]{
        // Constructing with immediately invoked lambda expression to allow storage of immutable ref
        CFMutableDictionaryRef matchingDict = CFDictionaryCreateMutable(
            kCFAllocatorDefault,
            1,
            &kCFTypeDictionaryKeyCallBacks,
            &kCFTypeDictionaryValueCallBacks);

        CFDictionaryAddValue(matchingDict, kDADiskDescriptionVolumeNetworkKey, kCFBooleanTrue);

        return matchingDict;
    }())
{}

bool NetworkDriveCallbacks::shouldNotify(CFDictionaryRef diskDescription) const noexcept
{
    auto volumeKind = reinterpret_cast<CFStringRef>(
        CFDictionaryGetValue(diskDescription, kDADiskDescriptionVolumeKindKey));

    return CFStringCompare(volumeKind, CFSTR("autofs"), 0) != kCFCompareEqualTo;
}

DriveNotifyOsx::DriveNotifyOsx()
    : mSession(DASessionCreate(kCFAllocatorDefault)),
    mPhysicalCbs(*this),
    mNetworkCbs(*this)
{}

bool DriveNotifyOsx::startNotifier()
{
    if (mEventSinkThread.joinable() || mStop.load()) return false;

    mPhysicalCbs.registerCallbacks(mSession);
    mNetworkCbs.registerCallbacks(mSession);

    mEventSinkThread = std::thread(&DriveNotifyOsx::doInThread, this);

    return true;
}

void DriveNotifyOsx::stopNotifier()
{
    if (!mEventSinkThread.joinable()) return;

    mStop.store(true);
    mEventSinkThread.join();

    mPhysicalCbs.unregisterCallbacks(mSession);
    mNetworkCbs.unregisterCallbacks(mSession);

    mStop.store(false);
}

void DriveNotifyOsx::doInThread()
{
    CFRunLoopRef currentThread = CFRunLoopGetCurrent();

    DASessionScheduleWithRunLoop(mSession, currentThread, kCFRunLoopDefaultMode);

    // CFRunLoopRunInMode will run for the special setting of 0 seconds, which means process
    // precisely one event before returning.
    while (!mStop.load() && CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true))
        ;

    CFRunLoopStop(currentThread);
}

} // namespace mega

#endif // USE_DRIVE_NOTIFICATIONS
<commit_msg>refactor for failure to get string from path<commit_after>/**
 * @file osx/drivenotifyosx.cpp
 * @brief Mega SDK various utilities and helper classes
 *
 * (c) 2013-2021 by Mega Limited, Auckland, New Zealand
 *
 * This file is part of the MEGA SDK - Client Access Engine.
 *
 * Applications using the MEGA API must present a valid application key
 * and comply with the the rules set forth in the Terms of Service.
 *
 * The MEGA SDK 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.
 *
 * @copyright Simplified (2-clause) BSD License.
 *
 * You should have received a copy of the license along with this
 * program.
 */

#ifdef USE_DRIVE_NOTIFICATIONS

#include "mega/drivenotify.h"

#include <sys/param.h>

namespace mega {

// CFArrayRef objects need to be constructed from const C-style array.
// We watch for changes in the Volume Path Key.
static const CFStringRef watchArray[1] = {kDADiskDescriptionVolumePathKey};

// See note here regarding callback registration/unregistration:
// https://developer.apple.com/library/archive/documentation/DriversKernelHardware/Conceptual/DiskArbitrationProgGuide/ArbitrationBasics/ArbitrationBasics.html#//apple_ref/doc/uid/TP40009310-CH2-SW9
// MediaTypeCallbacks is an abstract base class which uses the Non-Virtual Interface Pattern
// to implement callbacks, which are static member functions, where the `void* context` is a pointer
// to a concrete derived class. Per the note above, we are registering the same callback (i.e., pointer to static method)
// with a different context (i.e., pointer to derived class cast to void*)

void MediaTypeCallbacks::registerCallbacks(DASessionRef session)
{
    DARegisterDiskAppearedCallback(
        session,
        matchingDict(),
        &MediaTypeCallbacks::onDiskAppeared,
        this);

    DARegisterDiskDisappearedCallback(
        session,
        matchingDict(),
        &MediaTypeCallbacks::onDiskDisappeared,
        this);

    registerAdditionalCallbacks(session);
}

void MediaTypeCallbacks::unregisterCallbacks(DASessionRef session)
{
    DAUnregisterCallback(session, reinterpret_cast<void*>(&MediaTypeCallbacks::onDiskAppeared), this);
    DAUnregisterCallback(session, reinterpret_cast<void*>(&MediaTypeCallbacks::onDiskDisappeared), this);

    DAUnregisterCallback(session, reinterpret_cast<void*>(&MediaTypeCallbacks::onDiskDescriptionChanged), this);

    DAUnregisterApprovalCallback(session, reinterpret_cast<void*>(&MediaTypeCallbacks::onUnmountApproval), this);
}

void MediaTypeCallbacks::addDrive(CFURLRef path, bool connected)
{
    char buf[MAXPATHLEN];
    if (CFURLGetFileSystemRepresentation(path, false, reinterpret_cast<UInt8*>(buf), sizeof(buf)))
    {
        DriveInfo di;
        di.mountPoint = buf;
        di.connected = connected;
        mParent.add(std::move(di));
    }
}

void MediaTypeCallbacks::onDiskAppeared(DADiskRef disk, void* context)
{
    MediaTypeCallbacks& self = castSelf(context);

    UniqueCFRef<CFDictionaryRef> diskDescription = description(disk);

    if (!self.shouldNotify(diskDescription)) return;

    CFURLRef path = volumePath(diskDescription);

    if (path)
    {
        self.addDrive(path, true);
    }
    else
    {
        self.handleNoPathAppeared(diskDescription);
    }
}

void MediaTypeCallbacks::onDiskDisappeared(DADiskRef disk, void* context)
{
    MediaTypeCallbacks& self = castSelf(context);

    UniqueCFRef<CFDictionaryRef> diskDescription = description(disk);

    if (!self.shouldNotify(diskDescription)) return;

    CFURLRef path = volumePath(diskDescription);

    if (path) self.addDrive(path, false);

    self.processDisappeared(diskDescription);
}

DADissenterRef MediaTypeCallbacks::onUnmountApproval(DADiskRef disk, void* context)
{
    MediaTypeCallbacks::onDiskDisappeared(disk, context);
    return nullptr;
}

PhysicalMediaCallbacks::PhysicalMediaCallbacks(DriveNotifyOsx& parent)
    : MediaTypeCallbacks(parent),
     mMatchingDict([]{
        // Constructing with immediately invoked lambda expression to allow storage of immutable ref
        CFMutableDictionaryRef matchingDict = CFDictionaryCreateMutable(
            kCFAllocatorDefault,
            3,
            &kCFTypeDictionaryKeyCallBacks,
            &kCFTypeDictionaryValueCallBacks);

        // Do not match Whole Media, else we get multiple notifications for the same drive
        CFDictionaryAddValue(matchingDict, kDADiskDescriptionMediaWholeKey, kCFBooleanFalse);

        // Match removable/ejectable media
        CFDictionaryAddValue(matchingDict, kDADiskDescriptionMediaRemovableKey, kCFBooleanTrue);
        CFDictionaryAddValue(matchingDict, kDADiskDescriptionMediaEjectableKey, kCFBooleanTrue);

        return matchingDict;
    }()),
    mKeysToMonitor(CFArrayCreate(
        kCFAllocatorDefault,
        reinterpret_cast<const void**>(&watchArray),
        1,
        &kCFTypeArrayCallBacks))
{}

bool PhysicalMediaCallbacks::shouldNotify(CFDictionaryRef diskDescription) const noexcept
{
    auto deviceProtocol = reinterpret_cast<CFStringRef>(
        CFDictionaryGetValue(diskDescription, kDADiskDescriptionDeviceProtocolKey));

    return CFStringCompare(deviceProtocol, CFSTR(kIOPropertyPhysicalInterconnectTypeVirtual), 0) != kCFCompareEqualTo;
}

void PhysicalMediaCallbacks::registerAdditionalCallbacks(DASessionRef session)
{
    DARegisterDiskDescriptionChangedCallback(
        session,
        matchingDict(),
        mKeysToMonitor,
        &MediaTypeCallbacks::onDiskDescriptionChanged,
        this);

    DARegisterDiskUnmountApprovalCallback(
        session,
        matchingDict(),
        &MediaTypeCallbacks::onUnmountApproval,
        this);
}

void PhysicalMediaCallbacks::handleNoPathAppeared(CFDictionaryRef diskDescription)
{
    CFUUIDRef uuid = volumeUUID(diskDescription);
    if (uuid) mDisksPendingPath.insert(CFUUIDGetUUIDBytes(uuid));
}

void PhysicalMediaCallbacks::processDisappeared(CFDictionaryRef diskDescription)
{
    CFUUIDRef uuid = volumeUUID(diskDescription);
    if (uuid) mDisksPendingPath.erase(CFUUIDGetUUIDBytes(uuid));
}

// The actual onDiskDescriptionChanged callback allows for significantly greater generality than our use, which is
// basically just to try again performing the onDiskAppeared logic with slight modification
void PhysicalMediaCallbacks::onDiskDescriptionChangedImpl(DADiskRef disk, CFArrayRef matchingKeys, void* context)
{
    auto diskDescription = UniqueCFRef<CFDictionaryRef>(MediaTypeCallbacks::description(disk));

    CFURLRef path = volumePath(diskDescription);
    if (!path) return;

    CFUUIDRef uuid = volumeUUID(diskDescription);
    if (!uuid) return;

    auto findItr = mDisksPendingPath.find(CFUUIDGetUUIDBytes(uuid));
    if (findItr == mDisksPendingPath.end()) return;

    addDrive(path, true);

    mDisksPendingPath.erase(findItr);
}

NetworkDriveCallbacks::NetworkDriveCallbacks(DriveNotifyOsx& parent)
    : MediaTypeCallbacks(parent),
    mMatchingDict([]{
        // Constructing with immediately invoked lambda expression to allow storage of immutable ref
        CFMutableDictionaryRef matchingDict = CFDictionaryCreateMutable(
            kCFAllocatorDefault,
            1,
            &kCFTypeDictionaryKeyCallBacks,
            &kCFTypeDictionaryValueCallBacks);

        CFDictionaryAddValue(matchingDict, kDADiskDescriptionVolumeNetworkKey, kCFBooleanTrue);

        return matchingDict;
    }())
{}

bool NetworkDriveCallbacks::shouldNotify(CFDictionaryRef diskDescription) const noexcept
{
    auto volumeKind = reinterpret_cast<CFStringRef>(
        CFDictionaryGetValue(diskDescription, kDADiskDescriptionVolumeKindKey));

    return CFStringCompare(volumeKind, CFSTR("autofs"), 0) != kCFCompareEqualTo;
}

DriveNotifyOsx::DriveNotifyOsx()
    : mSession(DASessionCreate(kCFAllocatorDefault)),
    mPhysicalCbs(*this),
    mNetworkCbs(*this)
{}

bool DriveNotifyOsx::startNotifier()
{
    if (mEventSinkThread.joinable() || mStop.load()) return false;

    mPhysicalCbs.registerCallbacks(mSession);
    mNetworkCbs.registerCallbacks(mSession);

    mEventSinkThread = std::thread(&DriveNotifyOsx::doInThread, this);

    return true;
}

void DriveNotifyOsx::stopNotifier()
{
    if (!mEventSinkThread.joinable()) return;

    mStop.store(true);
    mEventSinkThread.join();

    mPhysicalCbs.unregisterCallbacks(mSession);
    mNetworkCbs.unregisterCallbacks(mSession);

    mStop.store(false);
}

void DriveNotifyOsx::doInThread()
{
    CFRunLoopRef currentThread = CFRunLoopGetCurrent();

    DASessionScheduleWithRunLoop(mSession, currentThread, kCFRunLoopDefaultMode);

    // CFRunLoopRunInMode will run for the special setting of 0 seconds, which means process
    // precisely one event before returning.
    while (!mStop.load() && CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true))
        ;

    CFRunLoopStop(currentThread);
}

} // namespace mega

#endif // USE_DRIVE_NOTIFICATIONS
<|endoftext|>


<commit_before>/*
 *	pAngle.cpp
 *
 *	Author: petel__
 *	Copyright (c) 2014-2016 HKUST SmartCar Team
 *	Refer to LICENSE for details
 *
 */

#include "pAngle.h"

using namespace libsc;
using namespace std;

Mma8451q::Config getAccelConfig(void)
{
	Mma8451q::Config config;
	config.id = 0;
	config.power_mode = Mma8451q::Config::PowerMode::kHighResolution;
	return config;
}

Mpu6050::Config getGyroConfig(void)
{
	Mpu6050::Config config;
	config.accel_range = Mpu6050::Config::Range::kLarge;
	config.gyro_range = Mpu6050::Config::Range::kSmall;
	config.cal_drift = true;
	return config;
}

pAngle::pAngle(pAngle::Config config)
:
	Mma8451q(getAccelConfig()),
	Mpu6050(getGyroConfig()),
	m_param(config),
	m_lastSpeed(0),
	m_lastAngle(0),
	m_lastYawOmega(0),
	m_gyroAngle(0),
	m_gyroOffset(0),
	m_lastTime(0)
{
	System::Init();
}

void pAngle::update(void)
{
	if (m_lastTime)
	{
		this->Mma8451q::UpdateF();
		this->Mpu6050::UpdateF();

		m_lastAccel = this->Mma8451q::GetAccelF();
		m_lastOmega = GetOmegaF();

		Timer::TimerInt dt = (System::Time() - m_lastTime) / 1000;

		m_gyroAngle -= m_lastOmega[0] * dt;
		m_gyroOffset += m_param.accelTrustValue * (asin(inRange(-1.0f, m_lastAccel[0], 1.0f)) * RadToDeg - m_lastAngle);
		m_lastSpeed = (m_gyroAngle + m_gyroOffset - m_lastAngle) / dt * m_param.cgHeight;
		m_lastAngle = m_gyroAngle + m_gyroOffset;
		m_lastYawOmega = m_lastOmega[2] * sin(m_lastAngle * DegToRad);
	}
	else
	{
		this->Mma8451q::UpdateF();
		m_lastAccel = this->Mma8451q::GetAccelF();
		m_lastAngle = m_gyroOffset = asin(inRange(-1.0f, m_lastAccel[0], 1.0f)) * RadToDeg;
	}

	m_lastTime = System::Time();
}

float pAngle::getAngle(void) const
{
	return m_lastAngle;
}

float pAngle::getSpeed(void) const
{
	return m_lastSpeed;
}

float pAngle::getYawOmega(void) const
{
	return m_lastYawOmega;
}

array<float, 3> pAngle::getAccel(void) const
{
	return m_lastAccel;
}

array<float, 3> pAngle::getOmega(void) const
{
	return m_lastOmega;
}

float &pAngle::getAccel(const uint8_t index)
{
	return m_lastAccel[index];
}

float &pAngle::getOmega(const uint8_t index)
{
	return m_lastOmega[index];
}
<commit_msg>Fix: dt in pAngle was always zero<commit_after>/*
 *	pAngle.cpp
 *
 *	Author: petel__
 *	Copyright (c) 2014-2016 HKUST SmartCar Team
 *	Refer to LICENSE for details
 *
 */

#include "pAngle.h"

using namespace libsc;
using namespace std;

Mma8451q::Config getAccelConfig(void)
{
	Mma8451q::Config config;
	config.id = 0;
	config.power_mode = Mma8451q::Config::PowerMode::kHighResolution;
	return config;
}

Mpu6050::Config getGyroConfig(void)
{
	Mpu6050::Config config;
	config.accel_range = Mpu6050::Config::Range::kLarge;
	config.gyro_range = Mpu6050::Config::Range::kSmall;
	config.cal_drift = true;
	return config;
}

pAngle::pAngle(pAngle::Config config)
:
	Mma8451q(getAccelConfig()),
	Mpu6050(getGyroConfig()),
	m_param(config),
	m_lastSpeed(0),
	m_lastAngle(0),
	m_lastYawOmega(0),
	m_gyroAngle(0),
	m_gyroOffset(0),
	m_lastTime(0)
{
	System::Init();
}

void pAngle::update(void)
{
	if (m_lastTime)
	{
		this->Mma8451q::UpdateF();
		this->Mpu6050::UpdateF();

		m_lastAccel = this->Mma8451q::GetAccelF();
		m_lastOmega = GetOmegaF();

		float dt = (System::Time() - m_lastTime) / 1000.0f;

		m_gyroAngle -= m_lastOmega[0] * dt;
		m_gyroOffset += m_param.accelTrustValue * (asin(inRange(-1.0f, m_lastAccel[0], 1.0f)) * RadToDeg - m_lastAngle);
		m_lastSpeed = (m_gyroAngle + m_gyroOffset - m_lastAngle) / dt * m_param.cgHeight;
		m_lastAngle = m_gyroAngle + m_gyroOffset;
		m_lastYawOmega = m_lastOmega[2] * sin(m_lastAngle * DegToRad);
	}
	else
	{
		this->Mma8451q::UpdateF();
		m_lastAccel = this->Mma8451q::GetAccelF();
		m_lastAngle = m_gyroOffset = asin(inRange(-1.0f, m_lastAccel[0], 1.0f)) * RadToDeg;
	}

	m_lastTime = System::Time();
}

float pAngle::getAngle(void) const
{
	return m_lastAngle;
}

float pAngle::getSpeed(void) const
{
	return m_lastSpeed;
}

float pAngle::getYawOmega(void) const
{
	return m_lastYawOmega;
}

array<float, 3> pAngle::getAccel(void) const
{
	return m_lastAccel;
}

array<float, 3> pAngle::getOmega(void) const
{
	return m_lastOmega;
}

float &pAngle::getAccel(const uint8_t index)
{
	return m_lastAccel[index];
}

float &pAngle::getOmega(const uint8_t index)
{
	return m_lastOmega[index];
}
<|endoftext|>


<commit_before>#ifndef ___INANITY_META_IMPL_HPP___
#define ___INANITY_META_IMPL_HPP___

#include "ClassBase.ipp"

// Before inclusion of this header, do the following:
// * include header of needed meta provider class
// * define a macro META_PROVIDER to a full name of the meta provider class
// After inclusion you likely want to undef META_PROVIDER.

#ifndef META_PROVIDER
#error META_PROVIDER isn't defined
#endif

#define META_CLASS(className, fullClassName) \
	template <> \
	class Inanity::Meta::Class<META_PROVIDER, className> : public META_PROVIDER::ClassBase \
	{ \
	private: \
		typedef META_PROVIDER Provider; \
		typedef className ClassType; \
	public: \
		Class(); \
	}; \
	template <> \
	META_PROVIDER::ClassBase* Inanity::Meta::MetaOf<META_PROVIDER, className>() \
	{ \
		static Class<META_PROVIDER, className> instance; \
		return &instance; \
	} \
	Inanity::Meta::Class<META_PROVIDER, className>::Class() \
		: META_PROVIDER::ClassBase(#className, #fullClassName) \
	{
#define META_CLASS_END() }

#define META_CLASS_PARENT(parentClassName) \
	SetParent(Inanity::Meta::MetaOf<Provider, parentClassName>())

#define META_CONSTRUCTOR(...) \
	{ \
		static Provider::Constructor<void (ClassType::*)(__VA_ARGS__)> c; \
		SetConstructor(&c); \
	}

#define META_METHOD(methodName) \
	{ \
		static Provider::Method<decltype(&ClassType::methodName), &ClassType::methodName> m(#methodName); \
		AddMethod(&m); \
	}

#define META_STATIC_METHOD(methodName) \
	{ \
		static Provider::Function<decltype(&ClassType::methodName), &ClassType::methodName> f(#methodName); \
		AddStaticMethod(&f); \
	}

#endif
<commit_msg>gcc fix: explicitly put metadata into namespace<commit_after>#ifndef ___INANITY_META_IMPL_HPP___
#define ___INANITY_META_IMPL_HPP___

#include "ClassBase.ipp"

// Before inclusion of this header, do the following:
// * include header of needed meta provider class
// * define a macro META_PROVIDER to a full name of the meta provider class
// After inclusion you likely want to undef META_PROVIDER.

#ifndef META_PROVIDER
#error "META_PROVIDER isn't defined"
#endif

#define META_CLASS(className, fullClassName) \
	BEGIN_INANITY_META \
	template <> \
	class Class<META_PROVIDER, className> : public META_PROVIDER::ClassBase \
	{ \
	private: \
		typedef META_PROVIDER Provider; \
		typedef className ClassType; \
	public: \
		Class(); \
	}; \
	template <> \
	META_PROVIDER::ClassBase* MetaOf<META_PROVIDER, className>() \
	{ \
		static Class<META_PROVIDER, className> instance; \
		return &instance; \
	} \
	Class<META_PROVIDER, className>::Class() \
		: META_PROVIDER::ClassBase(#className, #fullClassName) \
	{
#define META_CLASS_END() } END_INANITY_META

#define META_CLASS_PARENT(parentClassName) \
	SetParent(MetaOf<Provider, parentClassName>())

#define META_CONSTRUCTOR(...) \
	{ \
		static Provider::Constructor<void (ClassType::*)(__VA_ARGS__)> c; \
		SetConstructor(&c); \
	}

#define META_METHOD(methodName) \
	{ \
		static Provider::Method<decltype(&ClassType::methodName), &ClassType::methodName> m(#methodName); \
		AddMethod(&m); \
	}

#define META_STATIC_METHOD(methodName) \
	{ \
		static Provider::Function<decltype(&ClassType::methodName), &ClassType::methodName> f(#methodName); \
		AddStaticMethod(&f); \
	}

#endif
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2013-2020 Baptiste Wicht.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include "wishes.hpp"

#include "writer.hpp"
#include "pages/wishes_pages.hpp"
#include "http.hpp"

using namespace budget;

namespace {

void add_importance_picker(budget::writer& w, int importance) {
    w << R"=====(

                Importance
                <select class="form-control" id="input_importance" name="input_importance">
    )=====";

    if (importance == 1) {
        w << "<option selected value=\"1\">Low</option>";
    } else {
        w << "<option value=\"1\">Low</option>";
    }

    if (importance == 2) {
        w << "<option selected value=\"2\">Medium</option>";
    } else {
        w << "<option value=\"2\">Medium</option>";
    }

    if (importance == 3) {
        w << "<option selected value=\"3\">High</option>";
    } else {
        w << "<option value=\"3\">High</option>";
    }

    w << R"=====(
                </select>

    )=====";
}

void add_urgency_picker(budget::writer& w, int urgency) {
    w << R"=====(

                Urgency
                <select class="form-control" id="input_urgency" name="input_urgency">
    )=====";

    if (urgency == 1) {
        w << "<option selected value=\"1\">Low</option>";
    } else {
        w << "<option value=\"1\">Low</option>";
    }

    if (urgency == 2) {
        w << "<option selected value=\"2\">Medium</option>";
    } else {
        w << "<option value=\"2\">Medium</option>";
    }

    if (urgency == 3) {
        w << "<option selected value=\"3\">High</option>";
    } else {
        w << "<option value=\"3\">High</option>";
    }

    w << R"=====(
                </select>

    )=====";
}

} // namespace

void budget::wishes_list_page(const httplib::Request& req, httplib::Response& res) {
    std::stringstream content_stream;
    if (!page_start(req, res, content_stream, "Objectives List")) {
        return;
    }

    budget::html_writer w(content_stream);
    budget::list_wishes(w);

    make_tables_sortable(w);

    page_end(w, req, res);
}

void budget::wishes_status_page(const httplib::Request& req, httplib::Response& res) {
    std::stringstream content_stream;
    if (!page_start(req, res, content_stream, "Objectives Status")) {
        return;
    }

    budget::html_writer w(content_stream);
    budget::status_wishes(w);

    make_tables_sortable(w);

    page_end(w, req, res);
}

void budget::wishes_estimate_page(const httplib::Request& req, httplib::Response& res) {
    std::stringstream content_stream;
    if (!page_start(req, res, content_stream, "Objectives Status")) {
        return;
    }

    budget::html_writer w(content_stream);
    budget::estimate_wishes(w);

    make_tables_sortable(w);

    page_end(w, req, res);
}

void budget::add_wishes_page(const httplib::Request& req, httplib::Response& res) {
    std::stringstream content_stream;
    if (!page_start(req, res, content_stream, "New Wish")) {
        return;
    }

    budget::html_writer w(content_stream);

    w << title_begin << "New Wish" << title_end;

    form_begin(w, "/api/wishes/add/", "/wishes/add/");

    add_name_picker(w);
    add_importance_picker(w, 2);
    add_urgency_picker(w, 2);
    add_amount_picker(w);

    form_end(w);

    page_end(w, req, res);
}

void budget::edit_wishes_page(const httplib::Request& req, httplib::Response& res) {
    std::stringstream content_stream;

    if (!page_get_start(req, res, content_stream, "Edit Wish", {"input_id", "back_page"})){
        return;
    }

    budget::html_writer w(content_stream);

    auto input_id = req.get_param_value("input_id");

    if (!wish_exists(budget::to_number<size_t>(input_id))) {
        display_error_message(w, "The wish " + input_id + " does not exist");
    } else {
        auto back_page = req.get_param_value("back_page");

        w << title_begin << "Edit wish " << input_id << title_end;

        form_begin_edit(w, "/api/wishes/edit/", back_page, input_id);

        auto wish = wish_get(budget::to_number<size_t>(input_id));

        add_name_picker(w, wish.name);
        add_importance_picker(w, wish.importance);
        add_urgency_picker(w, wish.urgency);
        add_amount_picker(w, budget::money_to_string(wish.amount));
        add_paid_picker(w, wish.paid);
        add_paid_amount_picker(w, budget::money_to_string(wish.paid_amount));

        form_end(w);
    }

    page_end(w, req, res);
}
<commit_msg>Fix wishes pages titles<commit_after>//=======================================================================
// Copyright (c) 2013-2020 Baptiste Wicht.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include "wishes.hpp"

#include "writer.hpp"
#include "pages/wishes_pages.hpp"
#include "http.hpp"

using namespace budget;

namespace {

void add_importance_picker(budget::writer& w, int importance) {
    w << R"=====(

                Importance
                <select class="form-control" id="input_importance" name="input_importance">
    )=====";

    if (importance == 1) {
        w << "<option selected value=\"1\">Low</option>";
    } else {
        w << "<option value=\"1\">Low</option>";
    }

    if (importance == 2) {
        w << "<option selected value=\"2\">Medium</option>";
    } else {
        w << "<option value=\"2\">Medium</option>";
    }

    if (importance == 3) {
        w << "<option selected value=\"3\">High</option>";
    } else {
        w << "<option value=\"3\">High</option>";
    }

    w << R"=====(
                </select>

    )=====";
}

void add_urgency_picker(budget::writer& w, int urgency) {
    w << R"=====(

                Urgency
                <select class="form-control" id="input_urgency" name="input_urgency">
    )=====";

    if (urgency == 1) {
        w << "<option selected value=\"1\">Low</option>";
    } else {
        w << "<option value=\"1\">Low</option>";
    }

    if (urgency == 2) {
        w << "<option selected value=\"2\">Medium</option>";
    } else {
        w << "<option value=\"2\">Medium</option>";
    }

    if (urgency == 3) {
        w << "<option selected value=\"3\">High</option>";
    } else {
        w << "<option value=\"3\">High</option>";
    }

    w << R"=====(
                </select>

    )=====";
}

} // namespace

void budget::wishes_list_page(const httplib::Request& req, httplib::Response& res) {
    std::stringstream content_stream;
    if (!page_start(req, res, content_stream, "Wishes List")) {
        return;
    }

    budget::html_writer w(content_stream);
    budget::list_wishes(w);

    make_tables_sortable(w);

    page_end(w, req, res);
}

void budget::wishes_status_page(const httplib::Request& req, httplib::Response& res) {
    std::stringstream content_stream;
    if (!page_start(req, res, content_stream, "Wishes Status")) {
        return;
    }

    budget::html_writer w(content_stream);
    budget::status_wishes(w);

    make_tables_sortable(w);

    page_end(w, req, res);
}

void budget::wishes_estimate_page(const httplib::Request& req, httplib::Response& res) {
    std::stringstream content_stream;
    if (!page_start(req, res, content_stream, "Wishes Estimate")) {
        return;
    }

    budget::html_writer w(content_stream);
    budget::estimate_wishes(w);

    make_tables_sortable(w);

    page_end(w, req, res);
}

void budget::add_wishes_page(const httplib::Request& req, httplib::Response& res) {
    std::stringstream content_stream;
    if (!page_start(req, res, content_stream, "New Wish")) {
        return;
    }

    budget::html_writer w(content_stream);

    w << title_begin << "New Wish" << title_end;

    form_begin(w, "/api/wishes/add/", "/wishes/add/");

    add_name_picker(w);
    add_importance_picker(w, 2);
    add_urgency_picker(w, 2);
    add_amount_picker(w);

    form_end(w);

    page_end(w, req, res);
}

void budget::edit_wishes_page(const httplib::Request& req, httplib::Response& res) {
    std::stringstream content_stream;

    if (!page_get_start(req, res, content_stream, "Edit Wish", {"input_id", "back_page"})){
        return;
    }

    budget::html_writer w(content_stream);

    auto input_id = req.get_param_value("input_id");

    if (!wish_exists(budget::to_number<size_t>(input_id))) {
        display_error_message(w, "The wish " + input_id + " does not exist");
    } else {
        auto back_page = req.get_param_value("back_page");

        w << title_begin << "Edit wish " << input_id << title_end;

        form_begin_edit(w, "/api/wishes/edit/", back_page, input_id);

        auto wish = wish_get(budget::to_number<size_t>(input_id));

        add_name_picker(w, wish.name);
        add_importance_picker(w, wish.importance);
        add_urgency_picker(w, wish.urgency);
        add_amount_picker(w, budget::money_to_string(wish.amount));
        add_paid_picker(w, wish.paid);
        add_paid_amount_picker(w, budget::money_to_string(wish.paid_amount));

        form_end(w);
    }

    page_end(w, req, res);
}
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2018, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//      * Redistributions of source code must retain the above
//        copyright notice, this list of conditions and the following
//        disclaimer.
//
//      * Redistributions in binary form must reproduce the above
//        copyright notice, this list of conditions and the following
//        disclaimer in the documentation and/or other materials provided with
//        the distribution.
//
//      * Neither the name of John Haddon nor the names of
//        any other contributors to this software 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.
//
//////////////////////////////////////////////////////////////////////////

#include "boost/python.hpp"
#include "boost/python/tuple.hpp"

#include "SceneGadgetBinding.h"

#include "GafferSceneUI/SceneGadget.h"

#include "GafferUIBindings/GadgetBinding.h"

#include "GafferBindings/SignalBinding.h"

using namespace boost::python;
using namespace IECorePython;
using namespace Gaffer;
using namespace GafferScene;
using namespace GafferSceneUI;
using namespace GafferBindings;

namespace
{

void setScene( SceneGadget &g, ScenePlug &scene )
{
	ScopedGILRelease gilRelease;
	g.setScene( &scene );
}

GafferScene::ScenePlugPtr getScene( SceneGadget &g )
{
	return const_cast<GafferScene::ScenePlug *>( g.getScene() );
}

void setContext( SceneGadget &g, Context &context )
{
	ScopedGILRelease gilRelease;
	g.setContext( &context );
}

void setExpandedPaths( SceneGadget &g, const IECore::PathMatcher &expandedPaths )
{
	ScopedGILRelease gilRelease;
	g.setExpandedPaths( expandedPaths );
}

void setMinimumExpansionDepth( SceneGadget &g, size_t depth )
{
	ScopedGILRelease gilRelease;
	g.setMinimumExpansionDepth( depth );
}

void setPaused( SceneGadget &g, bool paused )
{
	ScopedGILRelease gilRelease;
	g.setPaused( paused );
}

void waitForCompletion( SceneGadget &g )
{
	ScopedGILRelease gilRelease;
	g.waitForCompletion();
}

struct SceneGadgetSlotCaller
{
	void operator()( boost::python::object slot, SceneGadgetPtr g )
	{
		try
		{
			slot( g );
		}
		catch( const error_already_set &e )
		{
			ExceptionAlgo::translatePythonException();
		}
	}
};

void setRenderer( SceneGadget &g, IECore::InternedString name )
{
	ScopedGILRelease gilRelease;
	g.setRenderer( name );
}

std::string getRenderer( SceneGadget &g )
{
	return g.getRenderer().string();
}

IECore::CompoundObjectPtr getOpenGLOptions( const SceneGadget &g )
{
	return g.getOpenGLOptions()->copy();
}

IECore::StringVectorDataPtr getSelectionMask( const SceneGadget &g )
{
	const IECore::StringVectorData *d = g.getSelectionMask();
	return d ? d->copy() : nullptr;
}

IECore::InternedStringVectorDataPtr objectAt( SceneGadget &g, IECore::LineSegment3f &l )
{
	IECore::InternedStringVectorDataPtr result = new IECore::InternedStringVectorData;
	if( g.objectAt( l, result->writable() ) )
	{
		return result;
	}
	return nullptr;
}

tuple objectAndIntersectionAt( SceneGadget &g, IECore::LineSegment3f &l )
{
	IECore::InternedStringVectorDataPtr result = new IECore::InternedStringVectorData;
	Imath::V3f hitPos( 0.0f );
	if( g.objectAt( l, result->writable(), hitPos ) )
	{
		return boost::python::make_tuple( result, hitPos );
	}
	return boost::python::make_tuple( object(), hitPos );
}

} // namespace

void GafferSceneUIModule::bindSceneGadget()
{

	scope s = GafferUIBindings::GadgetClass<SceneGadget>()
		.def( init<>() )
		.def( "setScene", &setScene )
		.def( "getScene", &getScene )
		.def( "setContext", &setContext )
		.def( "getContext", (Gaffer::Context *(SceneGadget::*)())&SceneGadget::getContext, return_value_policy<CastToIntrusivePtr>() )
		.def( "setExpandedPaths", &setExpandedPaths )
		.def( "getExpandedPaths", &SceneGadget::getExpandedPaths, return_value_policy<copy_const_reference>() )
		.def( "setMinimumExpansionDepth", &setMinimumExpansionDepth )
		.def( "getMinimumExpansionDepth", &SceneGadget::getMinimumExpansionDepth )
		.def( "getPaused", &SceneGadget::getPaused )
		.def( "setPaused", &setPaused )
		.def( "state", &SceneGadget::state )
		.def( "stateChangedSignal", &SceneGadget::stateChangedSignal, return_internal_reference<1>() )
		.def( "waitForCompletion", &waitForCompletion )
		.def( "setRenderer", &setRenderer )
		.def( "getRenderer", &getRenderer )
		.def( "setOpenGLOptions", &SceneGadget::setOpenGLOptions )
		.def( "getOpenGLOptions", &getOpenGLOptions )
		.def( "setSelectionMask", &SceneGadget::setSelectionMask )
		.def( "getSelectionMask", &getSelectionMask )
		.def( "objectAt", &objectAt )
		.def( "objectAndIntersectionAt", &objectAndIntersectionAt )
		.def( "objectsAt", &SceneGadget::objectsAt )
		.def( "setSelection", &SceneGadget::setSelection )
		.def( "getSelection", &SceneGadget::getSelection, return_value_policy<copy_const_reference>() )
		.def( "selectionBound", &SceneGadget::selectionBound )
		.def( "bound", (Imath::Box3f (SceneGadget::*)(bool,const IECore::PathMatcher*) const)&SceneGadget::bound,
			( arg( "selected" ), arg( "omitted" ) = object() )
		)
	;

	enum_<SceneGadget::State>( "State" )
		.value( "Paused", SceneGadget::Paused )
		.value( "Running", SceneGadget::Running )
		.value( "Complete", SceneGadget::Complete )
	;

	SignalClass<SceneGadget::SceneGadgetSignal, DefaultSignalCaller<SceneGadget::SceneGadgetSignal>, SceneGadgetSlotCaller>( "ImageGadgetSignal" );

}
<commit_msg>SceneGadgetBinding : Release GIL for `bound()` and `objectAt()`<commit_after>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2018, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//      * Redistributions of source code must retain the above
//        copyright notice, this list of conditions and the following
//        disclaimer.
//
//      * Redistributions in binary form must reproduce the above
//        copyright notice, this list of conditions and the following
//        disclaimer in the documentation and/or other materials provided with
//        the distribution.
//
//      * Neither the name of John Haddon nor the names of
//        any other contributors to this software 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.
//
//////////////////////////////////////////////////////////////////////////

#include "boost/python.hpp"
#include "boost/python/tuple.hpp"

#include "SceneGadgetBinding.h"

#include "GafferSceneUI/SceneGadget.h"

#include "GafferUIBindings/GadgetBinding.h"

#include "GafferBindings/SignalBinding.h"

using namespace boost::python;
using namespace IECorePython;
using namespace Gaffer;
using namespace GafferScene;
using namespace GafferSceneUI;
using namespace GafferBindings;

namespace
{

void setScene( SceneGadget &g, ScenePlug &scene )
{
	ScopedGILRelease gilRelease;
	g.setScene( &scene );
}

GafferScene::ScenePlugPtr getScene( SceneGadget &g )
{
	return const_cast<GafferScene::ScenePlug *>( g.getScene() );
}

void setContext( SceneGadget &g, Context &context )
{
	ScopedGILRelease gilRelease;
	g.setContext( &context );
}

void setExpandedPaths( SceneGadget &g, const IECore::PathMatcher &expandedPaths )
{
	ScopedGILRelease gilRelease;
	g.setExpandedPaths( expandedPaths );
}

void setMinimumExpansionDepth( SceneGadget &g, size_t depth )
{
	ScopedGILRelease gilRelease;
	g.setMinimumExpansionDepth( depth );
}

void setPaused( SceneGadget &g, bool paused )
{
	ScopedGILRelease gilRelease;
	g.setPaused( paused );
}

void waitForCompletion( SceneGadget &g )
{
	ScopedGILRelease gilRelease;
	g.waitForCompletion();
}

struct SceneGadgetSlotCaller
{
	void operator()( boost::python::object slot, SceneGadgetPtr g )
	{
		try
		{
			slot( g );
		}
		catch( const error_already_set &e )
		{
			ExceptionAlgo::translatePythonException();
		}
	}
};

void setRenderer( SceneGadget &g, IECore::InternedString name )
{
	ScopedGILRelease gilRelease;
	g.setRenderer( name );
}

std::string getRenderer( SceneGadget &g )
{
	return g.getRenderer().string();
}

IECore::CompoundObjectPtr getOpenGLOptions( const SceneGadget &g )
{
	return g.getOpenGLOptions()->copy();
}

IECore::StringVectorDataPtr getSelectionMask( const SceneGadget &g )
{
	const IECore::StringVectorData *d = g.getSelectionMask();
	return d ? d->copy() : nullptr;
}

IECore::InternedStringVectorDataPtr objectAt( SceneGadget &g, IECore::LineSegment3f &l )
{
	ScopedGILRelease gilRelease;
	IECore::InternedStringVectorDataPtr result = new IECore::InternedStringVectorData;
	if( g.objectAt( l, result->writable() ) )
	{
		return result;
	}
	return nullptr;
}

tuple objectAndIntersectionAt( SceneGadget &g, IECore::LineSegment3f &l )
{
	IECore::InternedStringVectorDataPtr result = new IECore::InternedStringVectorData;
	Imath::V3f hitPos( 0.0f );

	{
		ScopedGILRelease gilRelease;
		if( !g.objectAt( l, result->writable(), hitPos ) )
		{
			result = nullptr;
		}
	}

	return boost::python::make_tuple( result, hitPos );
}

size_t objectsAt( SceneGadget &g, const Imath::V3f &corner0InGadgetSpace, const Imath::V3f &corner1InGadgetSpace, IECore::PathMatcher &paths )
{
	ScopedGILRelease gilRelease;
	return g.objectsAt( corner0InGadgetSpace, corner1InGadgetSpace, paths );
}

Imath::Box3f selectionBound( SceneGadget &g )
{
	ScopedGILRelease gilRelease;
	return g.selectionBound();
}

Imath::Box3f bound( SceneGadget &g, bool selected, const IECore::PathMatcher *omitted )
{
	ScopedGILRelease gilRelease;
	return g.bound( selected, omitted );
}

} // namespace

void GafferSceneUIModule::bindSceneGadget()
{

	scope s = GafferUIBindings::GadgetClass<SceneGadget>()
		.def( init<>() )
		.def( "setScene", &setScene )
		.def( "getScene", &getScene )
		.def( "setContext", &setContext )
		.def( "getContext", (Gaffer::Context *(SceneGadget::*)())&SceneGadget::getContext, return_value_policy<CastToIntrusivePtr>() )
		.def( "setExpandedPaths", &setExpandedPaths )
		.def( "getExpandedPaths", &SceneGadget::getExpandedPaths, return_value_policy<copy_const_reference>() )
		.def( "setMinimumExpansionDepth", &setMinimumExpansionDepth )
		.def( "getMinimumExpansionDepth", &SceneGadget::getMinimumExpansionDepth )
		.def( "getPaused", &SceneGadget::getPaused )
		.def( "setPaused", &setPaused )
		.def( "state", &SceneGadget::state )
		.def( "stateChangedSignal", &SceneGadget::stateChangedSignal, return_internal_reference<1>() )
		.def( "waitForCompletion", &waitForCompletion )
		.def( "setRenderer", &setRenderer )
		.def( "getRenderer", &getRenderer )
		.def( "setOpenGLOptions", &SceneGadget::setOpenGLOptions )
		.def( "getOpenGLOptions", &getOpenGLOptions )
		.def( "setSelectionMask", &SceneGadget::setSelectionMask )
		.def( "getSelectionMask", &getSelectionMask )
		.def( "objectAt", &objectAt )
		.def( "objectAndIntersectionAt", &objectAndIntersectionAt )
		.def( "objectsAt", &objectsAt )
		.def( "setSelection", &SceneGadget::setSelection )
		.def( "getSelection", &SceneGadget::getSelection, return_value_policy<copy_const_reference>() )
		.def( "selectionBound", &selectionBound )
		.def( "bound", &bound, ( arg( "selected" ), arg( "omitted" ) = object() ) )
	;

	enum_<SceneGadget::State>( "State" )
		.value( "Paused", SceneGadget::Paused )
		.value( "Running", SceneGadget::Running )
		.value( "Complete", SceneGadget::Complete )
	;

	SignalClass<SceneGadget::SceneGadgetSignal, DefaultSignalCaller<SceneGadget::SceneGadgetSignal>, SceneGadgetSlotCaller>( "ImageGadgetSignal" );

}
<|endoftext|>


<commit_before>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2015 Scientific Computing and Imaging Institute,
   University of Utah.

   License for the specific language governing rights and limitations under
   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/

#include <QtGui>
#include <iostream>
#include <Dataflow/Network/ModuleDescription.h>
#include <Interface/Application/ModuleProxyWidget.h>
#include <Interface/Application/ModuleWidget.h>
#include <Interface/Application/Port.h>
#include <Interface/Application/Utility.h>
#include <Interface/Application/PositionProvider.h>
#include <Interface/Application/PortWidgetManager.h>
#include <Interface/Application/NetworkEditor.h>
#include <Core/Logging/Log.h>
#include <Core/Math/MiscMath2.h>
#include <Core/Application/Preferences/Preferences.h>

using namespace SCIRun::Gui;
using namespace SCIRun::Dataflow::Networks;
using namespace SCIRun::Core::Logging;
using namespace SCIRun::Core;

namespace SCIRun
{
  namespace Gui
  {
    class ModuleWidgetNoteDisplayStrategy : public NoteDisplayStrategy
    {
    public:
      virtual QPointF relativeNotePosition(QGraphicsItem* item, const QGraphicsTextItem* note, NotePosition position) const
      {
        const int noteMargin = 2;
        auto noteRect = note->boundingRect();
        auto thisRect = item->boundingRect();

        switch (position)
        {
        case None:
          {
            break;
          }
        case Top:
          {
            auto noteBottomMidpoint = (noteRect.bottomRight() + noteRect.bottomLeft()) / 2;
            auto noteBottomMidpointShift = noteRect.topLeft() - noteBottomMidpoint;
            auto moduleTopHalfLength = thisRect.width() / 2;
            noteBottomMidpointShift.rx() += moduleTopHalfLength;
            noteBottomMidpointShift.ry() -= noteMargin;
            return noteBottomMidpointShift;
          }
        case Bottom:
          {
            auto noteTopMidpoint = (noteRect.topRight() + noteRect.topLeft()) / 2;
            auto noteTopMidpointShift = noteRect.topLeft() - noteTopMidpoint;
            auto moduleTopHalfLength = thisRect.width() / 2;
            noteTopMidpointShift.rx() += moduleTopHalfLength;
            noteTopMidpointShift.ry() += thisRect.height() + noteMargin;
            return noteTopMidpointShift;
          }
        case Left:
          {
            auto noteRightMidpoint = (noteRect.topRight() + noteRect.bottomRight()) / 2;
            auto noteRightMidpointShift = noteRect.topLeft() - noteRightMidpoint;
            auto moduleSideHalfLength = thisRect.height() / 2;
            noteRightMidpointShift.rx() -= noteMargin;
            noteRightMidpointShift.ry() += moduleSideHalfLength;
            return noteRightMidpointShift;
          }
        case Right:
          {
            auto noteLeftMidpoint = (noteRect.topLeft() + noteRect.bottomLeft()) / 2;
            auto noteLeftMidpointShift = noteRect.topLeft() - noteLeftMidpoint;
            auto moduleSideHalfLength = thisRect.height() / 2;
            noteLeftMidpointShift.rx() += thisRect.width() + noteMargin;
            noteLeftMidpointShift.ry() += moduleSideHalfLength;
            return noteLeftMidpointShift;
          }
        case Tooltip:
          item->setToolTip(note->toHtml());
          break;
        case Default:
          break;
        }
        return QPointF();
      }
    };
  }
}

ModuleProxyWidget::ModuleProxyWidget(ModuleWidget* module, QGraphicsItem* parent/* = 0*/)
  : QGraphicsProxyWidget(parent),
  NoteDisplayHelper(boost::make_shared<ModuleWidgetNoteDisplayStrategy>()),
  module_(module),
  grabbedByWidget_(false),
  isSelected_(false),
  pressedSubWidget_(nullptr),
  doHighlight_(false)
{
  setWidget(module);
  setFlags(ItemIsMovable | ItemIsSelectable | ItemSendsGeometryChanges);
  setAcceptDrops(true);

  connect(module, SIGNAL(noteUpdated(const Note&)), this, SLOT(updateNote(const Note&)));
  connect(module, SIGNAL(requestModuleVisible()), this, SLOT(ensureThisVisible()));
  connect(module, SIGNAL(deleteMeLater()), this, SLOT(deleteLater()));
  connect(module, SIGNAL(executionDisabled(bool)), this, SLOT(disableModuleGUI(bool)));

  stackDepth_ = 0;

  originalSize_ = size();
  //qDebug() << "MPW1" << size();
  //adjustHeight(-15);
  //originalSize_ = size();
  //qDebug() << "MPW2" << size();
}

ModuleProxyWidget::~ModuleProxyWidget()
{
}

void ModuleProxyWidget::adjustHeight(int delta)
{
  auto p = pos();
  module_->setFixedHeight(originalSize_.height() + delta);
  setMaximumHeight(originalSize_.height() + delta);
  setPos(p);
}

void ModuleProxyWidget::adjustWidth(int delta)
{
  auto p = pos();
  module_->setFixedWidth(originalSize_.width() + delta);
  setMaximumWidth(originalSize_.width() + delta);
  setPos(p);
}

void ModuleProxyWidget::createStartupNote()
{
  module_->createStartupNote();
}

void ModuleProxyWidget::ensureThisVisible()
{
  ensureItemVisible(this);
}

void ModuleProxyWidget::ensureItemVisible(QGraphicsItem* item)
{
  auto views = scene()->views();
  if (!views.isEmpty())
  {
    auto netEd = qobject_cast<NetworkEditor*>(views[0]);
    if (netEd && netEd->currentZoomPercentage() > 200)
    {
      return; // the call below led to a crash when too zoomed in to fit a module.
    }
    views[0]->ensureVisible(item);
  }
}

void ModuleProxyWidget::updatePressedSubWidget(QGraphicsSceneMouseEvent* event)
{
  pressedSubWidget_ = widget()->childAt(event->pos().toPoint());
}

ModuleWidget* ModuleProxyWidget::getModuleWidget()
{
  return module_;
}

void ModuleProxyWidget::disableModuleGUI(bool disabled)
{
  if (disabled)
    setGraphicsEffect(blurEffect(3));
  else
    setGraphicsEffect(nullptr);
}

void ModuleProxyWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
  auto taggingOn = data(TagLayerKey).toBool();
  auto currentTag = data(CurrentTagKey).toInt();
  if (taggingOn && currentTag > NoTag)
  {
    auto thisTag = data(TagDataKey).toInt();
    auto newTag = currentTag == thisTag ? NoTag : currentTag;
    setData(TagDataKey, newTag);
    Q_EMIT tagChanged(newTag);
    return;
  }

  updatePressedSubWidget(event);

  if (PortWidget* p = qobject_cast<PortWidget*>(pressedSubWidget_))
  {
    p->doMousePress(event->button(), mapToScene(event->pos()));
    return;
  }

  if (isSubwidget(pressedSubWidget_))
  {
    QGraphicsItem::mousePressEvent(event);
    Q_EMIT selected();
    QGraphicsProxyWidget::mousePressEvent(event);
    grabbedByWidget_ = true;
  }
  else
  {
    QGraphicsItem::mousePressEvent(event);
    Q_EMIT selected();
    grabbedByWidget_ = false;
    position_ = pos();
  }
}

static int snapTo(int oldPos)
{
  using namespace SCIRun::Core::Math;
  const int strip = 76; // size of new background grid png
  const int shift = oldPos % strip;

  return oldPos - shift + (std::abs(shift) < strip/2 ? 0 : sgn(shift)*strip);
}

void ModuleProxyWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
  stackDepth_ = 0;
  auto taggingOn = data(TagLayerKey).toBool();
  if (taggingOn)
    return;

  if (PortWidget* p = qobject_cast<PortWidget*>(pressedSubWidget_))
  {
    p->doMouseRelease(event->button(), mapToScene(event->pos()), event->modifiers());
    return;
  }
  if (grabbedByWidget_)
  {
    QGraphicsProxyWidget::mouseReleaseEvent(event);
  }
  else
  {
    if (position_ != pos())
    {
      snapToGrid();
      Q_EMIT widgetMoved(ModuleId(module_->getModuleId()), pos().x(), pos().y());
    }
    QGraphicsItem::mouseReleaseEvent(event);
  }
  grabbedByWidget_ = false;
}

void ModuleProxyWidget::snapToGrid()
{
  if (Preferences::Instance().modulesSnapToGrid)
    setPos(snapTo(pos().x()), snapTo(pos().y()));
}

void ModuleProxyWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
  stackDepth_++;
  if (auto p = qobject_cast<PortWidget*>(pressedSubWidget_))
  {
    auto conn = p->doMouseMove(event->buttons(), mapToScene(event->pos()));
    if (conn)
    {
      if (stackDepth_ > 1)
        return;
      ensureItemVisible(conn);
    }
    stackDepth_ = 0;
    return;
  }
  if (grabbedByWidget_)
  {
    return;
  }
  if (stackDepth_ > 1)
    return;
  if (stackDepth_ == 0)
    ensureThisVisible();
  QGraphicsItem::mouseMoveEvent(event);
  stackDepth_ = 0;
}

bool ModuleProxyWidget::isSubwidget(QWidget* alienWidget) const
{
  return qobject_cast<QPushButton*>(alienWidget) ||
    qobject_cast<QToolButton*>(alienWidget) ||
    qobject_cast<QProgressBar*>(alienWidget);
}

void ModuleProxyWidget::highlightIfSelected()
{
  if (!isSelected_ && isSelected())
  {
    module_->setColorSelected();
    isSelected_ = true;
  }
  else if (isSelected_ && !isSelected())
  {
    module_->setColorUnselected();
    isSelected_ = false;
  }
  createPortPositionProviders();
}

QVariant ModuleProxyWidget::itemChange(GraphicsItemChange change, const QVariant& value)
{
  if (pos() != QPointF(0,0))
    cachedPosition_ = pos();

  if (change == ItemPositionHasChanged)
  {
    module_->trackConnections();
    updateNotePosition();
  }
  return QGraphicsItem::itemChange(change, value);
}

void ModuleProxyWidget::createPortPositionProviders()
{
  const int firstPortXPos = 5;
  Q_FOREACH(PortWidget* p, module_->ports().getAllPorts())
  {
    //qDebug() << "Setting position provider for port " << QString::fromStdString(p->id().toString()) << " at index " << p->getIndex() << " to " << firstPortXPos + (static_cast<int>(p->getIndex()) * (p->properWidth() + getModuleWidget()->portSpacing())) << "," << p->pos().y();
    //qDebug() << firstPortXPos << static_cast<int>(p->getIndex()) << p->properWidth() << getModuleWidget()->portSpacing();

    QPoint realPosition(firstPortXPos + (static_cast<int>(p->getIndex()) * (p->properWidth() + getModuleWidget()->portSpacing())), p->pos().y());

    int extraPadding = p->isHighlighted() ? 4 : 0;
    boost::shared_ptr<PositionProvider> pp(new ProxyWidgetPosition(this, realPosition + QPointF(p->properWidth() / 2 + extraPadding, 5)));
    //qDebug() << "PWP real " << realPosition + QPointF(p->properWidth() / 2 + extraPadding, 5);
    p->setPositionObject(pp);
  }
  if (pos() == QPointF(0, 0) && cachedPosition_ != pos())
  {
    setPos(cachedPosition_);
  }
}

void ModuleProxyWidget::updateNote(const Note& note)
{
  updateNoteImpl(note);
}

PassThroughPositioner::PassThroughPositioner(const QGraphicsProxyWidget* widget) : widget_(widget) {}

QPointF PassThroughPositioner::currentPosition() const
{
  return widget_->pos();
}

void ModuleProxyWidget::setNoteGraphicsContext()
{
  scene_ = scene();
  item_ = this;
  positioner_ = boost::make_shared<PassThroughPositioner>(this);
}

void ModuleProxyWidget::setDefaultNotePosition(NotePosition position)
{
  setDefaultNotePositionImpl(position);
}

void ModuleProxyWidget::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
{
  if (doHighlight_)
  {
    module_->highlightPorts();
    createPortPositionProviders();
  }
  QGraphicsProxyWidget::hoverEnterEvent(event);
}

void ModuleProxyWidget::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
{
  if (doHighlight_)
  {
    module_->unhighlightPorts();
    createPortPositionProviders();
  }
  QGraphicsProxyWidget::hoverLeaveEvent(event);
}

void ModuleProxyWidget::highlightPorts(int state)
{
  doHighlight_ = state != 0;
}
<commit_msg>Clean up. Closes #1367<commit_after>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2015 Scientific Computing and Imaging Institute,
   University of Utah.

   License for the specific language governing rights and limitations under
   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/

#include <QtGui>
#include <Dataflow/Network/ModuleDescription.h>
#include <Interface/Application/ModuleProxyWidget.h>
#include <Interface/Application/ModuleWidget.h>
#include <Interface/Application/Port.h>
#include <Interface/Application/Utility.h>
#include <Interface/Application/PositionProvider.h>
#include <Interface/Application/PortWidgetManager.h>
#include <Interface/Application/NetworkEditor.h>
#include <Core/Logging/Log.h>
#include <Core/Math/MiscMath2.h>
#include <Core/Application/Preferences/Preferences.h>

using namespace SCIRun::Gui;
using namespace SCIRun::Dataflow::Networks;
using namespace SCIRun::Core::Logging;
using namespace SCIRun::Core;

namespace SCIRun
{
  namespace Gui
  {
    class ModuleWidgetNoteDisplayStrategy : public NoteDisplayStrategy
    {
    public:
      virtual QPointF relativeNotePosition(QGraphicsItem* item, const QGraphicsTextItem* note, NotePosition position) const override
      {
        const int noteMargin = 2;
        auto noteRect = note->boundingRect();
        auto thisRect = item->boundingRect();

        switch (position)
        {
        case None:
          {
            break;
          }
        case Top:
          {
            auto noteBottomMidpoint = (noteRect.bottomRight() + noteRect.bottomLeft()) / 2;
            auto noteBottomMidpointShift = noteRect.topLeft() - noteBottomMidpoint;
            auto moduleTopHalfLength = thisRect.width() / 2;
            noteBottomMidpointShift.rx() += moduleTopHalfLength;
            noteBottomMidpointShift.ry() -= noteMargin;
            return noteBottomMidpointShift;
          }
        case Bottom:
          {
            auto noteTopMidpoint = (noteRect.topRight() + noteRect.topLeft()) / 2;
            auto noteTopMidpointShift = noteRect.topLeft() - noteTopMidpoint;
            auto moduleTopHalfLength = thisRect.width() / 2;
            noteTopMidpointShift.rx() += moduleTopHalfLength;
            noteTopMidpointShift.ry() += thisRect.height() + noteMargin;
            return noteTopMidpointShift;
          }
        case Left:
          {
            auto noteRightMidpoint = (noteRect.topRight() + noteRect.bottomRight()) / 2;
            auto noteRightMidpointShift = noteRect.topLeft() - noteRightMidpoint;
            auto moduleSideHalfLength = thisRect.height() / 2;
            noteRightMidpointShift.rx() -= noteMargin;
            noteRightMidpointShift.ry() += moduleSideHalfLength;
            return noteRightMidpointShift;
          }
        case Right:
          {
            auto noteLeftMidpoint = (noteRect.topLeft() + noteRect.bottomLeft()) / 2;
            auto noteLeftMidpointShift = noteRect.topLeft() - noteLeftMidpoint;
            auto moduleSideHalfLength = thisRect.height() / 2;
            noteLeftMidpointShift.rx() += thisRect.width() + noteMargin;
            noteLeftMidpointShift.ry() += moduleSideHalfLength;
            return noteLeftMidpointShift;
          }
        case Tooltip:
          item->setToolTip(note->toHtml());
          break;
        case Default:
          break;
        }
        return QPointF();
      }
    };
  }
}

ModuleProxyWidget::ModuleProxyWidget(ModuleWidget* module, QGraphicsItem* parent/* = 0*/)
  : QGraphicsProxyWidget(parent),
  NoteDisplayHelper(boost::make_shared<ModuleWidgetNoteDisplayStrategy>()),
  module_(module),
  grabbedByWidget_(false),
  isSelected_(false),
  pressedSubWidget_(nullptr),
  doHighlight_(false)
{
  setWidget(module);
  setFlags(ItemIsMovable | ItemIsSelectable | ItemSendsGeometryChanges);
  setAcceptDrops(true);

  connect(module, SIGNAL(noteUpdated(const Note&)), this, SLOT(updateNote(const Note&)));
  connect(module, SIGNAL(requestModuleVisible()), this, SLOT(ensureThisVisible()));
  connect(module, SIGNAL(deleteMeLater()), this, SLOT(deleteLater()));
  connect(module, SIGNAL(executionDisabled(bool)), this, SLOT(disableModuleGUI(bool)));

  stackDepth_ = 0;

  originalSize_ = size();
}

ModuleProxyWidget::~ModuleProxyWidget()
{
}

void ModuleProxyWidget::adjustHeight(int delta)
{
  auto p = pos();
  module_->setFixedHeight(originalSize_.height() + delta);
  setMaximumHeight(originalSize_.height() + delta);
  setPos(p);
}

void ModuleProxyWidget::adjustWidth(int delta)
{
  auto p = pos();
  module_->setFixedWidth(originalSize_.width() + delta);
  setMaximumWidth(originalSize_.width() + delta);
  setPos(p);
}

void ModuleProxyWidget::createStartupNote()
{
  module_->createStartupNote();
}

void ModuleProxyWidget::ensureThisVisible()
{
  ensureItemVisible(this);
}

void ModuleProxyWidget::ensureItemVisible(QGraphicsItem* item)
{
  auto views = scene()->views();
  if (!views.isEmpty())
  {
    auto netEd = qobject_cast<NetworkEditor*>(views[0]);
    if (netEd && netEd->currentZoomPercentage() > 200)
    {
      return; // the call below led to a crash when too zoomed in to fit a module.
    }
    views[0]->ensureVisible(item);
  }
}

void ModuleProxyWidget::updatePressedSubWidget(QGraphicsSceneMouseEvent* event)
{
  pressedSubWidget_ = widget()->childAt(event->pos().toPoint());
}

ModuleWidget* ModuleProxyWidget::getModuleWidget()
{
  return module_;
}

void ModuleProxyWidget::disableModuleGUI(bool disabled)
{
  if (disabled)
    setGraphicsEffect(blurEffect(3));
  else
    setGraphicsEffect(nullptr);
}

void ModuleProxyWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
  auto taggingOn = data(TagLayerKey).toBool();
  auto currentTag = data(CurrentTagKey).toInt();
  if (taggingOn && currentTag > NoTag)
  {
    auto thisTag = data(TagDataKey).toInt();
    auto newTag = currentTag == thisTag ? NoTag : currentTag;
    setData(TagDataKey, newTag);
    Q_EMIT tagChanged(newTag);
    return;
  }

  updatePressedSubWidget(event);

  if (auto p = qobject_cast<PortWidget*>(pressedSubWidget_))
  {
    p->doMousePress(event->button(), mapToScene(event->pos()));
    return;
  }

  if (isSubwidget(pressedSubWidget_))
  {
    QGraphicsItem::mousePressEvent(event);
    Q_EMIT selected();
    QGraphicsProxyWidget::mousePressEvent(event);
    grabbedByWidget_ = true;
  }
  else
  {
    QGraphicsItem::mousePressEvent(event);
    Q_EMIT selected();
    grabbedByWidget_ = false;
    position_ = pos();
  }
}

static int snapTo(int oldPos)
{
  using namespace Math;
  const int strip = 76; // size of new background grid png
  const int shift = oldPos % strip;

  return oldPos - shift + (std::abs(shift) < strip/2 ? 0 : sgn(shift)*strip);
}

void ModuleProxyWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
  stackDepth_ = 0;
  auto taggingOn = data(TagLayerKey).toBool();
  if (taggingOn)
    return;

  if (auto p = qobject_cast<PortWidget*>(pressedSubWidget_))
  {
    p->doMouseRelease(event->button(), mapToScene(event->pos()), event->modifiers());
    return;
  }
  if (grabbedByWidget_)
  {
    QGraphicsProxyWidget::mouseReleaseEvent(event);
  }
  else
  {
    if (position_ != pos())
    {
      snapToGrid();
      Q_EMIT widgetMoved(ModuleId(module_->getModuleId()), pos().x(), pos().y());
    }
    QGraphicsItem::mouseReleaseEvent(event);
  }
  grabbedByWidget_ = false;
}

void ModuleProxyWidget::snapToGrid()
{
  if (Preferences::Instance().modulesSnapToGrid)
    setPos(snapTo(pos().x()), snapTo(pos().y()));
}

void ModuleProxyWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
  stackDepth_++;
  if (auto p = qobject_cast<PortWidget*>(pressedSubWidget_))
  {
    auto conn = p->doMouseMove(event->buttons(), mapToScene(event->pos()));
    if (conn)
    {
      if (stackDepth_ > 1)
        return;
      ensureItemVisible(conn);
    }
    stackDepth_ = 0;
    return;
  }
  if (grabbedByWidget_)
  {
    return;
  }
  if (stackDepth_ > 1)
    return;
  if (stackDepth_ == 0)
    ensureThisVisible();
  QGraphicsItem::mouseMoveEvent(event);
  stackDepth_ = 0;
}

bool ModuleProxyWidget::isSubwidget(QWidget* alienWidget) const
{
  return qobject_cast<QPushButton*>(alienWidget) ||
    qobject_cast<QToolButton*>(alienWidget) ||
    qobject_cast<QProgressBar*>(alienWidget);
}

void ModuleProxyWidget::highlightIfSelected()
{
  if (!isSelected_ && isSelected())
  {
    module_->setColorSelected();
    isSelected_ = true;
  }
  else if (isSelected_ && !isSelected())
  {
    module_->setColorUnselected();
    isSelected_ = false;
  }
  createPortPositionProviders();
}

QVariant ModuleProxyWidget::itemChange(GraphicsItemChange change, const QVariant& value)
{
  if (pos() != QPointF(0,0))
    cachedPosition_ = pos();

  if (change == ItemPositionHasChanged)
  {
    module_->trackConnections();
    updateNotePosition();
  }
  return QGraphicsItem::itemChange(change, value);
}

void ModuleProxyWidget::createPortPositionProviders()
{
  const int firstPortXPos = 5;
  Q_FOREACH(PortWidget* p, module_->ports().getAllPorts())
  {
    //qDebug() << "Setting position provider for port " << QString::fromStdString(p->id().toString()) << " at index " << p->getIndex() << " to " << firstPortXPos + (static_cast<int>(p->getIndex()) * (p->properWidth() + getModuleWidget()->portSpacing())) << "," << p->pos().y();
    //qDebug() << firstPortXPos << static_cast<int>(p->getIndex()) << p->properWidth() << getModuleWidget()->portSpacing();

    QPoint realPosition(firstPortXPos + (static_cast<int>(p->getIndex()) * (p->properWidth() + getModuleWidget()->portSpacing())), p->pos().y());

    int extraPadding = p->isHighlighted() ? 4 : 0;
    boost::shared_ptr<PositionProvider> pp(new ProxyWidgetPosition(this, realPosition + QPointF(p->properWidth() / 2 + extraPadding, 5)));
    //qDebug() << "PWP real " << realPosition + QPointF(p->properWidth() / 2 + extraPadding, 5);
    p->setPositionObject(pp);
  }
  if (pos() == QPointF(0, 0) && cachedPosition_ != pos())
  {
    setPos(cachedPosition_);
  }
}

void ModuleProxyWidget::updateNote(const Note& note)
{
  updateNoteImpl(note);
}

PassThroughPositioner::PassThroughPositioner(const QGraphicsProxyWidget* widget) : widget_(widget) {}

QPointF PassThroughPositioner::currentPosition() const
{
  return widget_->pos();
}

void ModuleProxyWidget::setNoteGraphicsContext()
{
  scene_ = scene();
  item_ = this;
  positioner_ = boost::make_shared<PassThroughPositioner>(this);
}

void ModuleProxyWidget::setDefaultNotePosition(NotePosition position)
{
  setDefaultNotePositionImpl(position);
}

void ModuleProxyWidget::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
{
  if (doHighlight_)
  {
    module_->highlightPorts();
    createPortPositionProviders();
  }
  QGraphicsProxyWidget::hoverEnterEvent(event);
}

void ModuleProxyWidget::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
{
  if (doHighlight_)
  {
    module_->unhighlightPorts();
    createPortPositionProviders();
  }
  QGraphicsProxyWidget::hoverLeaveEvent(event);
}

void ModuleProxyWidget::highlightPorts(int state)
{
  doHighlight_ = state != 0;
}
<|endoftext|>


<commit_before>#include <QCoreApplication>
#include <QtWidgets/QAction>

#include "interpreter.h"

#include "details/autoconfigurer.h"
#include "details/robotImplementations/unrealRobotModelImplementation.h"
#include "details/robotCommunication/bluetoothRobotCommunicationThread.h"
#include "details/robotCommunication/usbRobotCommunicationThread.h"
#include "details/tracer.h"
#include "details/debugHelper.h"

using namespace qReal;
using namespace interpreters::robots;
using namespace interpreters::robots::details;

const Id startingElementType = Id("RobotsMetamodel", "RobotsDiagram", "InitialNode");
const Id startingElementType1 = Id("RobotsMetamodel", "RobotsDiagram", "InitialBlock");

Interpreter::Interpreter()
	: mGraphicalModelApi(NULL)
	, mLogicalModelApi(NULL)
	, mInterpretersInterface(NULL)
	, mState(idle)
	, mRobotModel(new RobotModel())
	, mBlocksTable(NULL)
	, mRobotCommunication(new RobotCommunicator(SettingsManager::value("valueOfCommunication").toString()))
	, mImplementationType(robotModelType::null)
	, mWatchListWindow(NULL)
	, mActionConnectToRobot(NULL)
{
	Tracer::enableAll();
	Tracer::setTarget(tracer::logFile);
	mParser = NULL;
	mBlocksTable = NULL;
	mTimer = new QTimer();

	mD2RobotModel = new d2Model::D2RobotModel();
	mD2ModelWidget = mD2RobotModel->createModelWidget();

	connect(mRobotModel, SIGNAL(disconnected()), this, SLOT(disconnectSlot()));
	connect(mRobotModel, SIGNAL(sensorsConfigured()), this, SLOT(sensorsConfiguredSlot()));
	connect(mRobotModel, SIGNAL(connected(bool)), this, SLOT(connectedSlot(bool)));
	connect(mD2ModelWidget, SIGNAL(d2WasClosed()), this, SLOT(stopRobot()));
	connect(mRobotCommunication, SIGNAL(errorOccured(QString)), this, SLOT(reportError(QString)));
}

void Interpreter::init(GraphicalModelAssistInterface const &graphicalModelApi
	, LogicalModelAssistInterface const &logicalModelApi
	, qReal::gui::MainWindowInterpretersInterface &interpretersInterface)
{
	mGraphicalModelApi = &graphicalModelApi;
	mLogicalModelApi = &logicalModelApi;
	mInterpretersInterface = &interpretersInterface;

	mParser = new RobotsBlockParser(mInterpretersInterface->errorReporter());
	mBlocksTable = new BlocksTable(graphicalModelApi, logicalModelApi, mRobotModel, mInterpretersInterface->errorReporter(), mParser);

	robotModelType::robotModelTypeEnum const modelType = static_cast<robotModelType::robotModelTypeEnum>(SettingsManager::value("robotModel").toInt());
	Tracer::debug(tracer::initialization, "Interpreter::init", "Going to set robot implementation, model type is " + DebugHelper::toString(modelType));
	setRobotImplementation(modelType);

	mWatchListWindow = new utils::WatchListWindow(mParser, mInterpretersInterface->windowWidget());
}

Interpreter::~Interpreter()
{
	foreach (Thread * const thread, mThreads) {
		delete thread;
	}
	delete mBlocksTable;
}

void Interpreter::interpret()
{
	Tracer::debug(tracer::initialization, "Interpreter::interpret", "Preparing for interpretation");

	mInterpretersInterface->errorReporter()->clear();

	Id const &currentDiagramId = mInterpretersInterface->activeDiagram();

	if (!mConnected) {
		mInterpretersInterface->errorReporter()->addInformation(tr("No connection to robot"));
		return;
	}
	if (mState != idle) {
		mInterpretersInterface->errorReporter()->addInformation(tr("Interpreter is already running"));
		return;
	}

	mBlocksTable->clear();
	mState = waitingForSensorsConfiguredToLaunch;
	mBlocksTable->setIdleForBlocks();

	Id const startingElement = findStartingElement(currentDiagramId);
	if (startingElement == Id()) {
		mInterpretersInterface->errorReporter()->addError(tr("No entry point found, please add Initial Node to a diagram"));
		mState = idle;
		return;
	}

	Autoconfigurer configurer(*mGraphicalModelApi, mBlocksTable, mInterpretersInterface->errorReporter(), mRobotModel);
	if (!configurer.configure(currentDiagramId)) {
		return;
	}
}

void Interpreter::stopRobot()
{
	mTimer->stop();
	mRobotModel->stopRobot();
	mState = idle;
	foreach (Thread *thread, mThreads) {
		delete thread;
		mThreads.removeAll(thread);
	}
	mBlocksTable->setFailure();
}

void Interpreter::showWatchList()
{
	mWatchListWindow->show();
}

void Interpreter::closeWatchList()
{
	if (mWatchListWindow) {
		mWatchListWindow->setVisible(false);
	}
}

void Interpreter::closeD2ModelWidget()
{
	if (mD2ModelWidget) {
		mD2ModelWidget->close();
	}
}

void Interpreter::showD2ModelWidget(bool isVisible)
{
	mD2ModelWidget->init(isVisible);
	if (isVisible) {
		mD2ModelWidget->activateWindow();
		mD2ModelWidget->showNormal();
	}
}

void Interpreter::setD2ModelWidgetActions(QAction *runAction, QAction *stopAction)
{
	mD2ModelWidget->setD2ModelWidgetActions(runAction, stopAction);
}

void Interpreter::enableD2ModelWidgetRunStopButtons()
{
	mD2ModelWidget->enableRunStopButtons();
}

void Interpreter::disableD2ModelWidgetRunStopButtons()
{
	mD2ModelWidget->disableRunStopButtons();
}

void Interpreter::setRobotImplementation(robotModelType::robotModelTypeEnum implementationType)
{
	mConnected = false;
	robotImplementations::AbstractRobotModelImplementation *robotImpl =
			robotImplementations::AbstractRobotModelImplementation::robotModel(implementationType, mRobotCommunication, mD2RobotModel);
	setRobotImplementation(robotImpl);
	mImplementationType = implementationType;
	if (mImplementationType != robotModelType::real) {
		mRobotModel->init();
	}
}

void Interpreter::connectedSlot(bool success)
{
	if (success) {
		if (mRobotModel->needsConnection()) {
			mInterpretersInterface->errorReporter()->addInformation(tr("Connected successfully"));
		}
	} else {
		Tracer::debug(tracer::initialization, "Interpreter::connectedSlot", "Robot connection status: " + QString::number(success));
		mInterpretersInterface->errorReporter()->addError(tr("Can't connect to a robot."));
	}
	mConnected = success;
	mActionConnectToRobot->setChecked(success);
}

void Interpreter::sensorsConfiguredSlot()
{
	Tracer::debug(tracer::initialization, "Interpreter::sensorsConfiguredSlot", "Sensors are configured");

	mConnected = true;
	mActionConnectToRobot->setChecked(mConnected);

	resetVariables();

	mRobotModel->nextBlockAfterInitial(mConnected);

	if (mState == waitingForSensorsConfiguredToLaunch) {
		mState = interpreting;

		runTimer();

		Tracer::debug(tracer::initialization, "Interpreter::sensorsConfiguredSlot", "Starting interpretation");
		mRobotModel->startInterpretation();

		Id const &currentDiagramId = mInterpretersInterface->activeDiagram();
		Id const startingElement = findStartingElement(currentDiagramId);
		Thread * const initialThread = new Thread(*mInterpretersInterface, *mBlocksTable, startingElement);
		addThread(initialThread);
	}
}

Id const Interpreter::findStartingElement(Id const &diagram) const
{
	IdList const children = mGraphicalModelApi->graphicalRepoApi().children(diagram);

	foreach (Id const child, children) {
		if (child.type() == startingElementType || child.type() == startingElementType1) {
			return child;
		}
	}

	return Id();
}

void Interpreter::threadStopped()
{
	Thread *thread = static_cast<Thread *>(sender());

	mThreads.removeAll(thread);
	delete thread;

	if (mThreads.isEmpty()) {
		stopRobot();
	}
}

void Interpreter::newThread(details::blocks::Block * const startBlock)
{
	Thread * const thread = new Thread(*mInterpretersInterface, *mBlocksTable, startBlock->id());
	addThread(thread);
}

void Interpreter::configureSensors(sensorType::SensorTypeEnum const &port1
		, sensorType::SensorTypeEnum const &port2
		, sensorType::SensorTypeEnum const &port3
		, sensorType::SensorTypeEnum const &port4)
{
	if (mConnected) {
		mRobotModel->configureSensors(port1, port2, port3, port4);
	}
}

void Interpreter::addThread(details::Thread * const thread)
{
	mThreads.append(thread);
	connect(thread, SIGNAL(stopped()), this, SLOT(threadStopped()));
	connect(thread, SIGNAL(newThread(details::blocks::Block*const)), this, SLOT(newThread(details::blocks::Block*const)));

	QCoreApplication::processEvents();
	thread->interpret();
}

interpreters::robots::details::RobotModel *Interpreter::robotModel()
{
	return mRobotModel;
}

void Interpreter::setRobotModel(details::RobotModel * const robotModel)
{
	mRobotModel = robotModel;
}

void Interpreter::setRobotImplementation(details::robotImplementations::AbstractRobotModelImplementation *robotImpl)
{
	mRobotModel->setRobotImplementation(robotImpl);
	if (robotImpl) {
		connect(mRobotModel, SIGNAL(connected(bool)), this, SLOT(runTimer()));
	}
}

void Interpreter::runTimer()
{
	if (!mTimer->isActive()) {
		mTimer->start(10);
		connect(mTimer, SIGNAL(timeout()), this, SLOT(readSensorValues()), Qt::UniqueConnection);
	}
	if (mRobotModel->sensor(inputPort::port1)) {
		connect(mRobotModel->sensor(inputPort::port1)->sensorImpl(), SIGNAL(response(int)), this, SLOT(responseSlot1(int)));
		connect(mRobotModel->sensor(inputPort::port1)->sensorImpl(), SIGNAL(failure()), this, SLOT(slotFailure()));
	}
	if (mRobotModel->sensor(inputPort::port2)) {
		connect(mRobotModel->sensor(inputPort::port2)->sensorImpl(), SIGNAL(response(int)), this, SLOT(responseSlot2(int)));
		connect(mRobotModel->sensor(inputPort::port2)->sensorImpl(), SIGNAL(failure()), this, SLOT(slotFailure()));
	}
	if (mRobotModel->sensor(inputPort::port3)) {
		connect(mRobotModel->sensor(inputPort::port3)->sensorImpl(), SIGNAL(response(int)), this, SLOT(responseSlot3(int)));
		connect(mRobotModel->sensor(inputPort::port3)->sensorImpl(), SIGNAL(failure()), this, SLOT(slotFailure()));
	}
	if (mRobotModel->sensor(inputPort::port4)) {
		connect(mRobotModel->sensor(inputPort::port4)->sensorImpl(), SIGNAL(response(int)), this, SLOT(responseSlot4(int)));
		connect(mRobotModel->sensor(inputPort::port4)->sensorImpl(), SIGNAL(failure()), this, SLOT(slotFailure()));
	}
}

void Interpreter::readSensorValues()
{
	if (mState == idle) {
		return;
	}

	if (mRobotModel->sensor(inputPort::port1)) {
		mRobotModel->sensor(inputPort::port1)->read();
	}
	if (mRobotModel->sensor(inputPort::port2)) {
		mRobotModel->sensor(inputPort::port2)->read();
	}
	if (mRobotModel->sensor(inputPort::port3)) {
		mRobotModel->sensor(inputPort::port3)->read();
	}
	if (mRobotModel->sensor(inputPort::port4)) {
		mRobotModel->sensor(inputPort::port4)->read();
	}
}

void Interpreter::slotFailure()
{
	Tracer::debug(tracer::autoupdatedSensorValues, "Interpreter::slotFailure", "");
}

void Interpreter::responseSlot1(int sensorValue)
{
	updateSensorValues("Sensor1", sensorValue);
}

void Interpreter::responseSlot2(int sensorValue)
{
	updateSensorValues("Sensor2", sensorValue);
}

void Interpreter::responseSlot3(int sensorValue)
{
	updateSensorValues("Sensor3", sensorValue);
}

void Interpreter::responseSlot4(int sensorValue)
{
	updateSensorValues("Sensor4", sensorValue);
}

void Interpreter::updateSensorValues(QString const &sensorVariableName, int sensorValue)
{
	(*(mParser->getVariables()))[sensorVariableName] = utils::Number(sensorValue, utils::Number::intType);
	Tracer::debug(tracer::autoupdatedSensorValues, "Interpreter::updateSensorValues", sensorVariableName + QString::number(sensorValue));
}

void Interpreter::resetVariables()
{
	int const resetValue = 0;
	responseSlot1(resetValue);
	responseSlot2(resetValue);
	responseSlot3(resetValue);
	responseSlot4(resetValue);
}

void Interpreter::connectToRobot()
{
	if (mConnected) {
		mRobotModel->stopRobot();
		mRobotModel->disconnectFromRobot();
	} else {
		mRobotModel->init();
		configureSensors(static_cast<sensorType::SensorTypeEnum>(SettingsManager::instance()->value("port1SensorType").toInt())
						 , static_cast<sensorType::SensorTypeEnum>(SettingsManager::instance()->value("port2SensorType").toInt())
						 , static_cast<sensorType::SensorTypeEnum>(SettingsManager::instance()->value("port3SensorType").toInt())
						 , static_cast<sensorType::SensorTypeEnum>(SettingsManager::instance()->value("port4SensorType").toInt()));
	}
	mActionConnectToRobot->setChecked(mConnected);
}

void Interpreter::disconnectSlot()
{
	mActionConnectToRobot->setChecked(false);
	mConnected = false;
}

void Interpreter::setRobotModelType(robotModelType::robotModelTypeEnum robotModelType)
{
	setRobotImplementation(robotModelType);
}

void Interpreter::setCommunicator(QString const &valueOfCommunication, QString const &portName)
{
	if (valueOfCommunication == mLastCommunicationValue) {
		return;
	}
	RobotCommunicationThreadInterface *communicator = NULL;
	if (valueOfCommunication == "bluetooth") {
		communicator = new BluetoothRobotCommunicationThread();
	} else {
		communicator = new UsbRobotCommunicationThread();
	}
	mLastCommunicationValue = valueOfCommunication;

	mRobotCommunication->setRobotCommunicationThreadObject(communicator);
	mRobotCommunication->setPortName(portName);
	connectToRobot();
}

void Interpreter::setConnectRobotAction(QAction *actionConnect)
{
	mActionConnectToRobot = actionConnect;
}

void Interpreter::reportError(QString const &message)
{
	mInterpretersInterface->errorReporter()->addError(message);
}

utils::WatchListWindow *Interpreter::watchWindow() const
{
	return mWatchListWindow;
}

void Interpreter::connectSensorConfigurer(details::SensorsConfigurationWidget *configurer) const
{
	connect(configurer, SIGNAL(saved()), mD2ModelWidget, SLOT(syncronizeSensors()));
}
<commit_msg>Fixed fault when disconnecting during interpretation<commit_after>#include <QCoreApplication>
#include <QtWidgets/QAction>

#include "interpreter.h"

#include "details/autoconfigurer.h"
#include "details/robotImplementations/unrealRobotModelImplementation.h"
#include "details/robotCommunication/bluetoothRobotCommunicationThread.h"
#include "details/robotCommunication/usbRobotCommunicationThread.h"
#include "details/tracer.h"
#include "details/debugHelper.h"

using namespace qReal;
using namespace interpreters::robots;
using namespace interpreters::robots::details;

const Id startingElementType = Id("RobotsMetamodel", "RobotsDiagram", "InitialNode");
const Id startingElementType1 = Id("RobotsMetamodel", "RobotsDiagram", "InitialBlock");

Interpreter::Interpreter()
	: mGraphicalModelApi(NULL)
	, mLogicalModelApi(NULL)
	, mInterpretersInterface(NULL)
	, mState(idle)
	, mRobotModel(new RobotModel())
	, mBlocksTable(NULL)
	, mRobotCommunication(new RobotCommunicator(SettingsManager::value("valueOfCommunication").toString()))
	, mImplementationType(robotModelType::null)
	, mWatchListWindow(NULL)
	, mActionConnectToRobot(NULL)
{
	Tracer::enableAll();
	Tracer::setTarget(tracer::logFile);
	mParser = NULL;
	mBlocksTable = NULL;
	mTimer = new QTimer();

	mD2RobotModel = new d2Model::D2RobotModel();
	mD2ModelWidget = mD2RobotModel->createModelWidget();

	connect(mRobotModel, SIGNAL(disconnected()), this, SLOT(disconnectSlot()));
	connect(mRobotModel, SIGNAL(sensorsConfigured()), this, SLOT(sensorsConfiguredSlot()));
	connect(mRobotModel, SIGNAL(connected(bool)), this, SLOT(connectedSlot(bool)));
	connect(mD2ModelWidget, SIGNAL(d2WasClosed()), this, SLOT(stopRobot()));
	connect(mRobotCommunication, SIGNAL(errorOccured(QString)), this, SLOT(reportError(QString)));
}

void Interpreter::init(GraphicalModelAssistInterface const &graphicalModelApi
	, LogicalModelAssistInterface const &logicalModelApi
	, qReal::gui::MainWindowInterpretersInterface &interpretersInterface)
{
	mGraphicalModelApi = &graphicalModelApi;
	mLogicalModelApi = &logicalModelApi;
	mInterpretersInterface = &interpretersInterface;

	mParser = new RobotsBlockParser(mInterpretersInterface->errorReporter());
	mBlocksTable = new BlocksTable(graphicalModelApi, logicalModelApi, mRobotModel, mInterpretersInterface->errorReporter(), mParser);

	robotModelType::robotModelTypeEnum const modelType = static_cast<robotModelType::robotModelTypeEnum>(SettingsManager::value("robotModel").toInt());
	Tracer::debug(tracer::initialization, "Interpreter::init", "Going to set robot implementation, model type is " + DebugHelper::toString(modelType));
	setRobotImplementation(modelType);

	mWatchListWindow = new utils::WatchListWindow(mParser, mInterpretersInterface->windowWidget());
}

Interpreter::~Interpreter()
{
	foreach (Thread * const thread, mThreads) {
		delete thread;
	}
	delete mBlocksTable;
}

void Interpreter::interpret()
{
	Tracer::debug(tracer::initialization, "Interpreter::interpret", "Preparing for interpretation");

	mInterpretersInterface->errorReporter()->clear();

	Id const &currentDiagramId = mInterpretersInterface->activeDiagram();

	if (!mConnected) {
		mInterpretersInterface->errorReporter()->addInformation(tr("No connection to robot"));
		return;
	}
	if (mState != idle) {
		mInterpretersInterface->errorReporter()->addInformation(tr("Interpreter is already running"));
		return;
	}

	mBlocksTable->clear();
	mState = waitingForSensorsConfiguredToLaunch;
	mBlocksTable->setIdleForBlocks();

	Id const startingElement = findStartingElement(currentDiagramId);
	if (startingElement == Id()) {
		mInterpretersInterface->errorReporter()->addError(tr("No entry point found, please add Initial Node to a diagram"));
		mState = idle;
		return;
	}

	Autoconfigurer configurer(*mGraphicalModelApi, mBlocksTable, mInterpretersInterface->errorReporter(), mRobotModel);
	if (!configurer.configure(currentDiagramId)) {
		return;
	}
}

void Interpreter::stopRobot()
{
	mTimer->stop();
	mRobotModel->stopRobot();
	mState = idle;
	foreach (Thread *thread, mThreads) {
		delete thread;
		mThreads.removeAll(thread);
	}
	mBlocksTable->setFailure();
}

void Interpreter::showWatchList()
{
	mWatchListWindow->show();
}

void Interpreter::closeWatchList()
{
	if (mWatchListWindow) {
		mWatchListWindow->setVisible(false);
	}
}

void Interpreter::closeD2ModelWidget()
{
	if (mD2ModelWidget) {
		mD2ModelWidget->close();
	}
}

void Interpreter::showD2ModelWidget(bool isVisible)
{
	mD2ModelWidget->init(isVisible);
	if (isVisible) {
		mD2ModelWidget->activateWindow();
		mD2ModelWidget->showNormal();
	}
}

void Interpreter::setD2ModelWidgetActions(QAction *runAction, QAction *stopAction)
{
	mD2ModelWidget->setD2ModelWidgetActions(runAction, stopAction);
}

void Interpreter::enableD2ModelWidgetRunStopButtons()
{
	mD2ModelWidget->enableRunStopButtons();
}

void Interpreter::disableD2ModelWidgetRunStopButtons()
{
	mD2ModelWidget->disableRunStopButtons();
}

void Interpreter::setRobotImplementation(robotModelType::robotModelTypeEnum implementationType)
{
	mConnected = false;
	robotImplementations::AbstractRobotModelImplementation *robotImpl =
			robotImplementations::AbstractRobotModelImplementation::robotModel(implementationType, mRobotCommunication, mD2RobotModel);
	setRobotImplementation(robotImpl);
	mImplementationType = implementationType;
	if (mImplementationType != robotModelType::real) {
		mRobotModel->init();
	}
}

void Interpreter::connectedSlot(bool success)
{
	if (success) {
		if (mRobotModel->needsConnection()) {
			mInterpretersInterface->errorReporter()->addInformation(tr("Connected successfully"));
		}
	} else {
		Tracer::debug(tracer::initialization, "Interpreter::connectedSlot", "Robot connection status: " + QString::number(success));
		mInterpretersInterface->errorReporter()->addError(tr("Can't connect to a robot."));
	}
	mConnected = success;
	mActionConnectToRobot->setChecked(success);
}

void Interpreter::sensorsConfiguredSlot()
{
	Tracer::debug(tracer::initialization, "Interpreter::sensorsConfiguredSlot", "Sensors are configured");

	mConnected = true;
	mActionConnectToRobot->setChecked(mConnected);

	resetVariables();

	mRobotModel->nextBlockAfterInitial(mConnected);

	if (mState == waitingForSensorsConfiguredToLaunch) {
		mState = interpreting;

		runTimer();

		Tracer::debug(tracer::initialization, "Interpreter::sensorsConfiguredSlot", "Starting interpretation");
		mRobotModel->startInterpretation();

		Id const &currentDiagramId = mInterpretersInterface->activeDiagram();
		Id const startingElement = findStartingElement(currentDiagramId);
		Thread * const initialThread = new Thread(*mInterpretersInterface, *mBlocksTable, startingElement);
		addThread(initialThread);
	}
}

Id const Interpreter::findStartingElement(Id const &diagram) const
{
	IdList const children = mGraphicalModelApi->graphicalRepoApi().children(diagram);

	foreach (Id const child, children) {
		if (child.type() == startingElementType || child.type() == startingElementType1) {
			return child;
		}
	}

	return Id();
}

void Interpreter::threadStopped()
{
	Thread *thread = static_cast<Thread *>(sender());

	mThreads.removeAll(thread);
	delete thread;

	if (mThreads.isEmpty()) {
		stopRobot();
	}
}

void Interpreter::newThread(details::blocks::Block * const startBlock)
{
	Thread * const thread = new Thread(*mInterpretersInterface, *mBlocksTable, startBlock->id());
	addThread(thread);
}

void Interpreter::configureSensors(sensorType::SensorTypeEnum const &port1
		, sensorType::SensorTypeEnum const &port2
		, sensorType::SensorTypeEnum const &port3
		, sensorType::SensorTypeEnum const &port4)
{
	if (mConnected) {
		mRobotModel->configureSensors(port1, port2, port3, port4);
	}
}

void Interpreter::addThread(details::Thread * const thread)
{
	mThreads.append(thread);
	connect(thread, SIGNAL(stopped()), this, SLOT(threadStopped()));
	connect(thread, SIGNAL(newThread(details::blocks::Block*const)), this, SLOT(newThread(details::blocks::Block*const)));

	QCoreApplication::processEvents();
	thread->interpret();
}

interpreters::robots::details::RobotModel *Interpreter::robotModel()
{
	return mRobotModel;
}

void Interpreter::setRobotModel(details::RobotModel * const robotModel)
{
	mRobotModel = robotModel;
}

void Interpreter::setRobotImplementation(details::robotImplementations::AbstractRobotModelImplementation *robotImpl)
{
	mRobotModel->setRobotImplementation(robotImpl);
	if (robotImpl) {
		connect(mRobotModel, SIGNAL(connected(bool)), this, SLOT(runTimer()));
	}
}

void Interpreter::runTimer()
{
	if (!mTimer->isActive()) {
		mTimer->start(10);
		connect(mTimer, SIGNAL(timeout()), this, SLOT(readSensorValues()), Qt::UniqueConnection);
	}
	if (mRobotModel->sensor(inputPort::port1)) {
		connect(mRobotModel->sensor(inputPort::port1)->sensorImpl(), SIGNAL(response(int)), this, SLOT(responseSlot1(int)));
		connect(mRobotModel->sensor(inputPort::port1)->sensorImpl(), SIGNAL(failure()), this, SLOT(slotFailure()));
	}
	if (mRobotModel->sensor(inputPort::port2)) {
		connect(mRobotModel->sensor(inputPort::port2)->sensorImpl(), SIGNAL(response(int)), this, SLOT(responseSlot2(int)));
		connect(mRobotModel->sensor(inputPort::port2)->sensorImpl(), SIGNAL(failure()), this, SLOT(slotFailure()));
	}
	if (mRobotModel->sensor(inputPort::port3)) {
		connect(mRobotModel->sensor(inputPort::port3)->sensorImpl(), SIGNAL(response(int)), this, SLOT(responseSlot3(int)));
		connect(mRobotModel->sensor(inputPort::port3)->sensorImpl(), SIGNAL(failure()), this, SLOT(slotFailure()));
	}
	if (mRobotModel->sensor(inputPort::port4)) {
		connect(mRobotModel->sensor(inputPort::port4)->sensorImpl(), SIGNAL(response(int)), this, SLOT(responseSlot4(int)));
		connect(mRobotModel->sensor(inputPort::port4)->sensorImpl(), SIGNAL(failure()), this, SLOT(slotFailure()));
	}
}

void Interpreter::readSensorValues()
{
	if (mState == idle) {
		return;
	}

	if (mRobotModel->sensor(inputPort::port1)) {
		mRobotModel->sensor(inputPort::port1)->read();
	}
	if (mRobotModel->sensor(inputPort::port2)) {
		mRobotModel->sensor(inputPort::port2)->read();
	}
	if (mRobotModel->sensor(inputPort::port3)) {
		mRobotModel->sensor(inputPort::port3)->read();
	}
	if (mRobotModel->sensor(inputPort::port4)) {
		mRobotModel->sensor(inputPort::port4)->read();
	}
}

void Interpreter::slotFailure()
{
	Tracer::debug(tracer::autoupdatedSensorValues, "Interpreter::slotFailure", "");
}

void Interpreter::responseSlot1(int sensorValue)
{
	updateSensorValues("Sensor1", sensorValue);
}

void Interpreter::responseSlot2(int sensorValue)
{
	updateSensorValues("Sensor2", sensorValue);
}

void Interpreter::responseSlot3(int sensorValue)
{
	updateSensorValues("Sensor3", sensorValue);
}

void Interpreter::responseSlot4(int sensorValue)
{
	updateSensorValues("Sensor4", sensorValue);
}

void Interpreter::updateSensorValues(QString const &sensorVariableName, int sensorValue)
{
	(*(mParser->getVariables()))[sensorVariableName] = utils::Number(sensorValue, utils::Number::intType);
	Tracer::debug(tracer::autoupdatedSensorValues, "Interpreter::updateSensorValues", sensorVariableName + QString::number(sensorValue));
}

void Interpreter::resetVariables()
{
	int const resetValue = 0;
	responseSlot1(resetValue);
	responseSlot2(resetValue);
	responseSlot3(resetValue);
	responseSlot4(resetValue);
}

void Interpreter::connectToRobot()
{
	if (mState == interpreting) {
		return;
	}
	if (mConnected) {
		mRobotModel->stopRobot();
		mRobotModel->disconnectFromRobot();
	} else {
		mRobotModel->init();
		configureSensors(static_cast<sensorType::SensorTypeEnum>(SettingsManager::instance()->value("port1SensorType").toInt())
						 , static_cast<sensorType::SensorTypeEnum>(SettingsManager::instance()->value("port2SensorType").toInt())
						 , static_cast<sensorType::SensorTypeEnum>(SettingsManager::instance()->value("port3SensorType").toInt())
						 , static_cast<sensorType::SensorTypeEnum>(SettingsManager::instance()->value("port4SensorType").toInt()));
	}
	mActionConnectToRobot->setChecked(mConnected);
}

void Interpreter::disconnectSlot()
{
	mActionConnectToRobot->setChecked(false);
	mConnected = false;
}

void Interpreter::setRobotModelType(robotModelType::robotModelTypeEnum robotModelType)
{
	setRobotImplementation(robotModelType);
}

void Interpreter::setCommunicator(QString const &valueOfCommunication, QString const &portName)
{
	if (valueOfCommunication == mLastCommunicationValue) {
		return;
	}
	RobotCommunicationThreadInterface *communicator = NULL;
	if (valueOfCommunication == "bluetooth") {
		communicator = new BluetoothRobotCommunicationThread();
	} else {
		communicator = new UsbRobotCommunicationThread();
	}
	mLastCommunicationValue = valueOfCommunication;

	mRobotCommunication->setRobotCommunicationThreadObject(communicator);
	mRobotCommunication->setPortName(portName);
	connectToRobot();
}

void Interpreter::setConnectRobotAction(QAction *actionConnect)
{
	mActionConnectToRobot = actionConnect;
}

void Interpreter::reportError(QString const &message)
{
	mInterpretersInterface->errorReporter()->addError(message);
}

utils::WatchListWindow *Interpreter::watchWindow() const
{
	return mWatchListWindow;
}

void Interpreter::connectSensorConfigurer(details::SensorsConfigurationWidget *configurer) const
{
	connect(configurer, SIGNAL(saved()), mD2ModelWidget, SLOT(syncronizeSensors()));
}
<|endoftext|>