<commit_before>#pragma once

#include <gcl/mp/meta/functional.hpp>

namespace gcl::mp
{
    template <std::size_t index>
    struct type_index {};

    template <typename... types>
    struct tuple {

        constexpr static auto size = sizeof...(types);
        constexpr static auto empty = size == 0;

        constexpr tuple() requires(not empty)
            : storage{generate_storage(types{}...)}
        {}
        constexpr tuple(types&&... values)
            : storage{generate_storage(std::forward<decltype(values)>(values)...)}
        {}
        constexpr tuple(tuple&&) = default;

      private:
        // storage : by-pass missing features : auto non-static members, lambdas in unevaluated context
        using index_sequence = std::make_index_sequence<sizeof...(types)>;
        static constexpr auto generate_storage(types&&... values)
        { // defer parameter pack expansion (msvc-cl, Clang)
            return generate_storage_impl(index_sequence{}, std::forward<decltype(values)>(values)...);
        }
        template <std::size_t... indexes>
        static constexpr auto generate_storage_impl(std::index_sequence<indexes...>, types&&... values)
        {
            static_assert(sizeof...(indexes) == sizeof...(types));
            static_assert(sizeof...(indexes) == sizeof...(values));

            const auto generate_storage_entry = []<typename T, std::size_t index>(T && init_value) constexpr
            {
                return [value = init_value](type_index<index>) constexpr mutable noexcept -> auto&& { return value; };
            };
            return gcl::mp::meta::functional::overload{
                generate_storage_entry.template operator()<types, indexes>(std::forward<decltype(values)>(values))...};
        };
        using storage_type = decltype(generate_storage(types{}...));
        mutable storage_type storage;

        template <std::size_t index>
        struct type_at_impl { // defer symbol (Clang)
            using type = std::remove_reference_t<decltype(std::declval<tuple>().template get<index>())>;
        };

      public:
        template <std::size_t index>
        using type_at = typename type_at_impl<index>::type;

        template <std::size_t index>
        requires(not empty and index <= (size - 1)) constexpr auto& get() & noexcept
        {
            return storage(type_index<index>{});
        }
        template <std::size_t index>
        requires(not empty and index <= (size - 1)) constexpr const auto& get() const & noexcept
        {
            return storage(type_index<index>{});
        }
        template <std::size_t index>
        requires(not empty and index <= (size - 1)) constexpr auto&& get() && noexcept
        {
            return std::move(storage(type_index<index>{}));
        }
        template <std::size_t index>
        requires(not empty and index <= (size - 1)) constexpr const auto&& get() const && noexcept
        {
            return std::move(storage(type_index<index>{}));
        }

        template <typename... arg_types>
        requires(sizeof...(arg_types) == size) constexpr bool operator==(
            const tuple<arg_types...>& other) const noexcept
        {
            return [ this, &other ]<std::size_t... indexes>(std::index_sequence<indexes...>)
            {
                return (((get<indexes>() == other.template get<indexes>()) && ...));
            }
            (std::make_index_sequence<size>{});
        }
    };
    // When Clang, Msvc-cl gets better, replace `tuple::generate_storage` implementation by :
    /*static constexpr auto generate_storage(types&&... values)
    {
        return [&values...]<std::size_t... indexes>(std::index_sequence<indexes...>)
        {
            static_assert(sizeof...(indexes) == sizeof...(types));
            static_assert(sizeof...(indexes) == sizeof...(values));

            const auto generate_storage_entry = []<typename T, std::size_t index>(T && init_value) constexpr
            {
                return [value = init_value](type_index<index>) mutable -> auto& { return value; };
            };
            return gcl::mp::meta::functional::overload{generate_storage_entry.template operator()<types, indexes>(
                std::forward<decltype(values)>(values))...};
        }
        (std::make_index_sequence<sizeof...(types)>{});
    };*/
}

namespace gcl::mp
{
    template <std::size_t I, class... Types>
    constexpr auto & get(tuple<Types...>& value) noexcept
    {
        return value.template get<I>();
    }

    template <std::size_t I, class... Types>
    constexpr auto && get(tuple<Types...>&& value) noexcept
    {
        return std::move(value.template get<I>());
    }
    template <std::size_t I, class... Types>
    constexpr auto const& get(const tuple<Types...>& value) noexcept
    {
        return value.template get<I>();
    }
    template <std::size_t I, class... Types>
    constexpr auto const&& get(const tuple<Types...>&& value) noexcept
    {
        return std::move(value.template get<I>());
    }

    template <class T, class... Types>
    constexpr T& get(tuple<Types...>& value) noexcept;
    template <class T, class... Types>
    constexpr T&& get(tuple<Types...>&& value) noexcept;
    template <class T, class... Types>
    constexpr const T& get(const tuple<Types...>& value) noexcept;
    template <class T, class... Types>
    constexpr const T&& get(const tuple<Types...>&& value) noexcept;
}
namespace gcl::mp::tests::tuples::get
{
    using namespace gcl::mp;
    using tuple_type = tuple<int, char>;

    static_assert(std::is_same_v<char&, decltype(std::declval<tuple_type&>().get<1>())>);
    static_assert(std::is_same_v<char&&, decltype(std::declval<tuple_type&&>().get<1>())>);
    static_assert(std::is_same_v<const char&, decltype(std::declval<const tuple_type&>().get<1>())>);
    static_assert(std::is_same_v<const char&&, decltype(std::declval<const tuple_type&&>().get<1>())>);

    static_assert(std::is_same_v<char&, decltype(gcl::mp::get<1>(std::declval<tuple_type&>()))>);
    static_assert(std::is_same_v<char&&, decltype(gcl::mp::get<1>(std::declval<tuple_type&&>()))>);
    static_assert(std::is_same_v<const char&, decltype(gcl::mp::get<1>(std::declval<const tuple_type&>()))>);
    static_assert(std::is_same_v<const char&&, decltype(gcl::mp::get<1>(std::declval<const tuple_type&&>()))>);
}

#if defined(GCL_ENABLE_COMPILE_TIME_TESTS)
#include <stdexcept>
#include <exception>
namespace gcl::mp::tests::tuples
{
    using namespace gcl::mp;

    using empty_tuple = tuple<>;
    using one_element_tuple = tuple<int>;
    using two_element_tuple = tuple<int, char>;

    static constexpr auto empty_tuple_default_init = empty_tuple{};
    static constexpr auto one_element_tuple_default_init = one_element_tuple{};
    static constexpr auto two_element_tuple_default_init = two_element_tuple{};

    static constexpr auto one_element_tuple_values_init = one_element_tuple{42};
    static constexpr auto two_element_tuple_values_init = two_element_tuple{42, 'a'};

    static_assert(std::is_same_v<tuple<int, char>, decltype(tuple{42, 'a'})>);

    static_assert(std::is_same_v<two_element_tuple::type_at<0>, int>);
    static_assert(std::is_same_v<two_element_tuple::type_at<1>, char>);

    static_assert(std::is_same_v<decltype(std::declval<two_element_tuple&>().get<0>()), int&>);
    static_assert(std::is_same_v<decltype(std::declval<const two_element_tuple&>().get<1>()), const char&>);
    static_assert(std::is_same_v<decltype(std::declval<two_element_tuple>().get<0>()), int&&>);
    static_assert(std::is_same_v<decltype(std::declval<two_element_tuple&&>().get<0>()), int&&>);

    void non_literal_type(){
        struct can_throw_constructor {
            can_throw_constructor() { throw std::runtime_error{""}; };
        };
        using faillible_tuple = tuple<can_throw_constructor>;
        [[maybe_unused]] const auto faillible_tuple_default_init = faillible_tuple{};
        [[maybe_unused]] const auto faillible_tuple_value_init = faillible_tuple{can_throw_constructor{}};
    }
}
#endif<commit_msg>[mp/meta/tuple] : cleanup requires clause -> requires(valid_index<index>)<commit_after>#pragma once

#include <gcl/mp/meta/functional.hpp>

namespace gcl::mp
{
    template <std::size_t index>
    struct type_index {};

    template <typename... types>
    struct tuple {

        constexpr static auto size = sizeof...(types);
        constexpr static auto empty = size == 0;
        template <std::size_t index>
        constexpr static auto valid_index = not empty and index <= (size - 1);

        constexpr tuple() requires(not empty)
            : storage{generate_storage(types{}...)}
        {}
        constexpr tuple(types&&... values)
            : storage{generate_storage(std::forward<decltype(values)>(values)...)}
        {}
        constexpr tuple(tuple&&) = default;

      private:

        // storage : by-pass missing features : auto non-static members, lambdas in unevaluated context
        using index_sequence = std::make_index_sequence<sizeof...(types)>;
        static constexpr auto generate_storage(types&&... values)
        { // defer parameter pack expansion (msvc-cl, Clang)
            return generate_storage_impl(index_sequence{}, std::forward<decltype(values)>(values)...);
        }
        template <std::size_t... indexes>
        static constexpr auto generate_storage_impl(std::index_sequence<indexes...>, types&&... values)
        {
            static_assert(sizeof...(indexes) == sizeof...(types));
            static_assert(sizeof...(indexes) == sizeof...(values));

            const auto generate_storage_entry = []<typename T, std::size_t index>(T && init_value) constexpr
            {
                return [value = init_value](type_index<index>) constexpr mutable noexcept -> auto&& { return value; };
            };
            return gcl::mp::meta::functional::overload{
                generate_storage_entry.template operator()<types, indexes>(std::forward<decltype(values)>(values))...};
        };
        using storage_type = decltype(generate_storage(types{}...));
        mutable storage_type storage;

        template <std::size_t index>
        requires(valid_index<index>)
        struct type_at_impl { // defer symbol (Clang)
            using type = std::remove_reference_t<decltype(std::declval<tuple>().template get<index>())>;
        };

      public:
        template <std::size_t index>
        requires(valid_index<index>)
        using type_at = typename type_at_impl<index>::type;

        template <std::size_t index>
        requires(valid_index<index>)
        constexpr auto& get() & noexcept
        {
            return storage(type_index<index>{});
        }
        template <std::size_t index>
        requires(valid_index<index>) constexpr const auto& get() const& noexcept
        {
            return storage(type_index<index>{});
        }
        template <std::size_t index>
        requires(valid_index<index>) constexpr auto&& get() && noexcept
        {
            return std::move(storage(type_index<index>{}));
        }
        template <std::size_t index>
        requires(valid_index<index>) constexpr const auto&& get() const&& noexcept
        {
            return std::move(storage(type_index<index>{}));
        }

        template <typename... arg_types>
        requires(sizeof...(arg_types) == size) constexpr bool operator==(
            const tuple<arg_types...>& other) const noexcept
        {
            return [ this, &other ]<std::size_t... indexes>(std::index_sequence<indexes...>)
            {
                return (((get<indexes>() == other.template get<indexes>()) && ...));
            }
            (std::make_index_sequence<size>{});
        }
    };
    // When Clang, Msvc-cl gets better, replace `tuple::generate_storage` implementation by :
    /*static constexpr auto generate_storage(types&&... values)
    {
        return [&values...]<std::size_t... indexes>(std::index_sequence<indexes...>)
        {
            static_assert(sizeof...(indexes) == sizeof...(types));
            static_assert(sizeof...(indexes) == sizeof...(values));

            const auto generate_storage_entry = []<typename T, std::size_t index>(T && init_value) constexpr
            {
                return [value = init_value](type_index<index>) mutable -> auto& { return value; };
            };
            return gcl::mp::meta::functional::overload{generate_storage_entry.template operator()<types, indexes>(
                std::forward<decltype(values)>(values))...};
        }
        (std::make_index_sequence<sizeof...(types)>{});
    };*/
}

namespace gcl::mp
{
    template <std::size_t I, class... Types>
    constexpr auto & get(tuple<Types...>& value) noexcept
    {
        return value.template get<I>();
    }

    template <std::size_t I, class... Types>
    constexpr auto && get(tuple<Types...>&& value) noexcept
    {
        return std::move(value.template get<I>());
    }
    template <std::size_t I, class... Types>
    constexpr auto const& get(const tuple<Types...>& value) noexcept
    {
        return value.template get<I>();
    }
    template <std::size_t I, class... Types>
    constexpr auto const&& get(const tuple<Types...>&& value) noexcept
    {
        return std::move(value.template get<I>());
    }

    template <class T, class... Types>
    constexpr T& get(tuple<Types...>& value) noexcept;
    template <class T, class... Types>
    constexpr T&& get(tuple<Types...>&& value) noexcept;
    template <class T, class... Types>
    constexpr const T& get(const tuple<Types...>& value) noexcept;
    template <class T, class... Types>
    constexpr const T&& get(const tuple<Types...>&& value) noexcept;
}
namespace gcl::mp::tests::tuples::get
{
    using namespace gcl::mp;
    using tuple_type = tuple<int, char>;

    static_assert(std::is_same_v<char&, decltype(std::declval<tuple_type&>().get<1>())>);
    static_assert(std::is_same_v<char&&, decltype(std::declval<tuple_type&&>().get<1>())>);
    static_assert(std::is_same_v<const char&, decltype(std::declval<const tuple_type&>().get<1>())>);
    static_assert(std::is_same_v<const char&&, decltype(std::declval<const tuple_type&&>().get<1>())>);

    static_assert(std::is_same_v<char&, decltype(gcl::mp::get<1>(std::declval<tuple_type&>()))>);
    static_assert(std::is_same_v<char&&, decltype(gcl::mp::get<1>(std::declval<tuple_type&&>()))>);
    static_assert(std::is_same_v<const char&, decltype(gcl::mp::get<1>(std::declval<const tuple_type&>()))>);
    static_assert(std::is_same_v<const char&&, decltype(gcl::mp::get<1>(std::declval<const tuple_type&&>()))>);
}

#if defined(GCL_ENABLE_COMPILE_TIME_TESTS)
#include <stdexcept>
#include <exception>
namespace gcl::mp::tests::tuples
{
    using namespace gcl::mp;

    using empty_tuple = tuple<>;
    using one_element_tuple = tuple<int>;
    using two_element_tuple = tuple<int, char>;

    static constexpr auto empty_tuple_default_init = empty_tuple{};
    static constexpr auto one_element_tuple_default_init = one_element_tuple{};
    static constexpr auto two_element_tuple_default_init = two_element_tuple{};

    static constexpr auto one_element_tuple_values_init = one_element_tuple{42};
    static constexpr auto two_element_tuple_values_init = two_element_tuple{42, 'a'};

    static_assert(std::is_same_v<tuple<int, char>, decltype(tuple{42, 'a'})>);

    static_assert(std::is_same_v<two_element_tuple::type_at<0>, int>);
    static_assert(std::is_same_v<two_element_tuple::type_at<1>, char>);

    static_assert(std::is_same_v<decltype(std::declval<two_element_tuple&>().get<0>()), int&>);
    static_assert(std::is_same_v<decltype(std::declval<const two_element_tuple&>().get<1>()), const char&>);
    static_assert(std::is_same_v<decltype(std::declval<two_element_tuple>().get<0>()), int&&>);
    static_assert(std::is_same_v<decltype(std::declval<two_element_tuple&&>().get<0>()), int&&>);

    void non_literal_type(){
        struct can_throw_constructor {
            can_throw_constructor() { throw std::runtime_error{""}; };
        };
        using faillible_tuple = tuple<can_throw_constructor>;
        [[maybe_unused]] const auto faillible_tuple_default_init = faillible_tuple{};
        [[maybe_unused]] const auto faillible_tuple_value_init = faillible_tuple{can_throw_constructor{}};
    }
}
#endif<|endoftext|>


<commit_before>/**
 * @file llviewerchat.cpp
 * @brief Builds menus out of items.
 *
 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h"
#include "llviewerchat.h"

// newview includes
#include "llagent.h" 	// gAgent
#include "llslurl.h"
#include "lluicolor.h"
#include "lluicolortable.h"
#include "llviewercontrol.h" // gSavedSettings
#include "llviewerregion.h"
#include "llworld.h"
#include "llinstantmessage.h" //SYSTEM_FROM
#include "fskeywords.h"
#include "lggcontactsets.h"
#include "rlvhandler.h"

#include "growlmanager.h" // <FS:LO> Growl include

// LLViewerChat
LLViewerChat::font_change_signal_t LLViewerChat::sChatFontChangedSignal;

//static
void LLViewerChat::getChatColor(const LLChat& chat, LLColor4& r_color, bool is_local)
{
	if(chat.mMuted)
	{
		r_color= LLUIColorTable::instance().getColor("LtGray");
	}
	else
	{
		switch(chat.mSourceType)
		{
			case CHAT_SOURCE_SYSTEM:
				r_color = LLUIColorTable::instance().getColor("SystemChatColor");
				break;
			case CHAT_SOURCE_AGENT:
				if (chat.mFromID.isNull() || SYSTEM_FROM == chat.mFromName)
				{
					r_color = LLUIColorTable::instance().getColor("SystemChatColor");
				}
				else
				{
					if(gAgentID == chat.mFromID)
					{
						r_color = LLUIColorTable::instance().getColor("UserChatColor");
					}
					else
					{
						r_color = LLUIColorTable::instance().getColor("AgentChatColor");
					}

					// <FS:CR> FIRE-1061 - Color friends, lindens, muted, etc
					static LLUICachedControl<bool> fs_colorize("FSColorizeChat");
					if (fs_colorize)
						r_color = LGGContactSets::getInstance()->getSpecialColor(chat.mFromID, r_color);
					// </FS:CR>

					//color based on contact sets prefs
					if(LGGContactSets::getInstance()->hasFriendColorThatShouldShow(chat.mFromID, LGG_CS_CHAT))
					{
						r_color = LGGContactSets::getInstance()->getFriendColor(chat.mFromID);
					}
				}
				break;
			case CHAT_SOURCE_OBJECT:
				if (chat.mChatType == CHAT_TYPE_DEBUG_MSG)
				{
					r_color = LLUIColorTable::instance().getColor("ScriptErrorColor");
				}
				else if ( chat.mChatType == CHAT_TYPE_OWNER )
				{
					r_color = LLUIColorTable::instance().getColor("llOwnerSayChatColor");
				}
				else if ( chat.mChatType == CHAT_TYPE_DIRECT )
				{
					r_color = LLUIColorTable::instance().getColor("DirectChatColor");
				}
				else if ( chat.mChatType == CHAT_TYPE_IM )
				{
					r_color = LLUIColorTable::instance().getColor("ObjectIMColor");
					// <FS:LO> FIRE-5889: Object IM's Not Triggering Growl Notifications
					std::string msg = chat.mFromName;
					std::string prefix = chat.mText.substr(0, 4);
					if(prefix == "/me " || prefix == "/me'")
					{
						msg = msg + chat.mText.substr(3);
					}
					else
					{
						msg = msg + ": " + chat.mText;
					}
					gGrowlManager->notify(chat.mFromName, msg, GROWL_IM_MESSAGE_TYPE);
					// </FS:LO>
				}
				else
				{
					r_color = LLUIColorTable::instance().getColor("ObjectChatColor");
				}
				break;
			default:
				r_color.setToWhite();
		}

		//Keyword alerts -KC
		if ((gAgentID != chat.mFromID || chat.mFromName == SYSTEM_FROM) && FSKeywords::getInstance()->chatContainsKeyword(chat, is_local))
		{
			std::string msg = chat.mFromName;
			std::string prefix = chat.mText.substr(0, 4);
			if(prefix == "/me " || prefix == "/me'")
			{
				msg = msg + chat.mText.substr(3);
			}
			else
			{
				msg = msg + ": " + chat.mText;
			}

			gGrowlManager->notify("Keyword Alert", msg, "Keyword Alert");

			static LLCachedControl<bool> sFSKeywordChangeColor(gSavedPerAccountSettings, "FSKeywordChangeColor");
			if (sFSKeywordChangeColor)
			{
				static LLCachedControl<LLColor4> sFSKeywordColor(gSavedPerAccountSettings, "FSKeywordColor");
				r_color = sFSKeywordColor;
			}
		}

		if (!chat.mPosAgent.isExactlyZero())
		{
			LLVector3 pos_agent = gAgent.getPositionAgent();
			F32 distance_squared = dist_vec_squared(pos_agent, chat.mPosAgent);
// <FS:CR> Aurora Sim
			//F32 dist_near_chat = gAgent.getNearChatRadius();
			//if (!avatarp || dist_vec_squared(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > say_distance_squared)
			F32 dist_near_chat = LLWorld::getInstance()->getSayDistance();
// </FS:CR> Aurora Sim
			if (distance_squared > dist_near_chat * dist_near_chat)
			{
				// diminish far-off chat
				r_color.mV[VALPHA] = 0.8f;
			}
		}
	}
}

//static
void LLViewerChat::getChatColor(const LLChat& chat, std::string& r_color_name, F32& r_color_alpha)
{
	if(chat.mMuted)
	{
		r_color_name = "LtGray";
	}
	else
	{
		switch(chat.mSourceType)
		{
			case CHAT_SOURCE_SYSTEM:
				r_color_name = "SystemChatColor";
				break;

			case CHAT_SOURCE_AGENT:
				if (chat.mFromID.isNull())
				{
					r_color_name = "SystemChatColor";
				}
				else
				{
					if(gAgentID == chat.mFromID)
					{
						r_color_name = "UserChatColor";
					}
					else
					{
						r_color_name = "AgentChatColor";
					}
				}
				break;

			case CHAT_SOURCE_OBJECT:
				if (chat.mChatType == CHAT_TYPE_DEBUG_MSG)
				{
					r_color_name = "ScriptErrorColor";
				}
				else if ( chat.mChatType == CHAT_TYPE_OWNER )
				{
					r_color_name = "llOwnerSayChatColor";
				}
				else if ( chat.mChatType == CHAT_TYPE_DIRECT )
				{
					r_color_name = "DirectChatColor";
				}
				else if ( chat.mChatType == CHAT_TYPE_IM )
				{
					r_color_name = "ObjectIMColor";
				}
				else
				{
					r_color_name = "ObjectChatColor";
				}
				break;
			default:
				r_color_name = "White";
		}

		if (!chat.mPosAgent.isExactlyZero())
		{
			LLVector3 pos_agent = gAgent.getPositionAgent();
			F32 distance_squared = dist_vec_squared(pos_agent, chat.mPosAgent);
// <FS:CR> Aurora som
			//F32 dist_near_chat = gAgent.getNearChatRadius();
			F32 dist_near_chat = LLWorld::getInstance()->getSayDistance();
// </FS:CR> Aurora sim
			if (distance_squared > dist_near_chat * dist_near_chat)
			{
				// diminish far-off chat
				r_color_alpha = 0.8f;
			}
			else
			{
				r_color_alpha = 1.0f;
			}
		}
	}

}

//static
LLFontGL* LLViewerChat::getChatFont()
{
	S32 font_size = gSavedSettings.getS32("ChatFontSize");
	LLFontGL* fontp = NULL;
	switch(font_size)
	{
		case 0:
			fontp = LLFontGL::getFontSansSerifSmall();
			break;
		default:
		case 1:
			fontp = LLFontGL::getFontSansSerif();
			break;
		case 2:
			fontp = LLFontGL::getFontSansSerifBig();
			break;
		case 3:
			fontp = LLFontGL::getFontSansSerifHuge();
			break;
	}

	return fontp;

}

//static
S32 LLViewerChat::getChatFontSize()
{
	return gSavedSettings.getS32("ChatFontSize");
}

//static
void LLViewerChat::formatChatMsg(const LLChat& chat, std::string& formated_msg)
{
	std::string tmpmsg = chat.mText;

	if(chat.mChatStyle == CHAT_STYLE_IRC)
	{
		formated_msg = chat.mFromName + tmpmsg.substr(3);
	}
	else
	{
		formated_msg = tmpmsg;
	}

}

//static
std::string LLViewerChat::getSenderSLURL(const LLChat& chat, const LLSD& args)
{
	switch (chat.mSourceType)
	{
	case CHAT_SOURCE_AGENT:
		return LLSLURL("agent", chat.mFromID, "about").getSLURLString();

	case CHAT_SOURCE_OBJECT:
		return getObjectImSLURL(chat, args);

	// <FS:Ansariel> Stop spamming the log when processing system messages
	case CHAT_SOURCE_SYSTEM:
		return LLStringUtil::null;
	// </FS:Ansariel>

	default:
		llwarns << "Getting SLURL for an unsupported sender type: " << chat.mSourceType << llendl;
	}

	return LLStringUtil::null;
}

//static
std::string LLViewerChat::getObjectImSLURL(const LLChat& chat, const LLSD& args)
{
	std::string url = LLSLURL("objectim", chat.mFromID, "").getSLURLString();
	url += "?name=" + chat.mFromName;
	url += "&owner=" + chat.mOwnerID.asString();

	std::string slurl = args["slurl"].asString();
	if (slurl.empty())
	{
		LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosAgent(chat.mPosAgent);
		if(region)
		{
			LLSLURL region_slurl(region->getName(), chat.mPosAgent);
			slurl = region_slurl.getLocationString();
		}
	}

	url += "&slurl=" + LLURI::escape(slurl);

	return url;
}

//static
boost::signals2::connection LLViewerChat::setFontChangedCallback(const font_change_signal_t::slot_type& cb)
{
	return sChatFontChangedSignal.connect(cb);
}

//static
void LLViewerChat::signalChatFontChanged()
{
	// Notify all observers that our font has changed
	sChatFontChangedSignal(getChatFont());
}
<commit_msg>More FIRE-1061: Don't use UserChatColor for chat unless we enable it<commit_after>/**
 * @file llviewerchat.cpp
 * @brief Builds menus out of items.
 *
 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h"
#include "llviewerchat.h"

// newview includes
#include "llagent.h" 	// gAgent
#include "llslurl.h"
#include "lluicolor.h"
#include "lluicolortable.h"
#include "llviewercontrol.h" // gSavedSettings
#include "llviewerregion.h"
#include "llworld.h"
#include "llinstantmessage.h" //SYSTEM_FROM
#include "fskeywords.h"
#include "lggcontactsets.h"
#include "rlvhandler.h"

#include "growlmanager.h" // <FS:LO> Growl include

// LLViewerChat
LLViewerChat::font_change_signal_t LLViewerChat::sChatFontChangedSignal;

//static
void LLViewerChat::getChatColor(const LLChat& chat, LLColor4& r_color, bool is_local)
{
	if(chat.mMuted)
	{
		r_color= LLUIColorTable::instance().getColor("LtGray");
	}
	else
	{
		switch(chat.mSourceType)
		{
			case CHAT_SOURCE_SYSTEM:
				r_color = LLUIColorTable::instance().getColor("SystemChatColor");
				break;
			case CHAT_SOURCE_AGENT:
				if (chat.mFromID.isNull() || SYSTEM_FROM == chat.mFromName)
				{
					r_color = LLUIColorTable::instance().getColor("SystemChatColor");
				}
				else
				{
					// <FS:CR> FIRE-1061 - Color friends, lindens, muted, etc
					// Handle "UserChatColor" through the colorizer
					//if(gAgentID == chat.mFromID)
					//{
					//	r_color = LLUIColorTable::instance().getColor("UserChatColor");
					//}
					//else
					//{
						r_color = LLUIColorTable::instance().getColor("AgentChatColor");
					//}
					static LLUICachedControl<bool> fs_colorize("FSColorizeChat");
					if (fs_colorize)
						r_color = LGGContactSets::getInstance()->getSpecialColor(chat.mFromID, r_color);
					// </FS:CR>

					//color based on contact sets prefs
					if(LGGContactSets::getInstance()->hasFriendColorThatShouldShow(chat.mFromID, LGG_CS_CHAT))
					{
						r_color = LGGContactSets::getInstance()->getFriendColor(chat.mFromID);
					}
				}
				break;
			case CHAT_SOURCE_OBJECT:
				if (chat.mChatType == CHAT_TYPE_DEBUG_MSG)
				{
					r_color = LLUIColorTable::instance().getColor("ScriptErrorColor");
				}
				else if ( chat.mChatType == CHAT_TYPE_OWNER )
				{
					r_color = LLUIColorTable::instance().getColor("llOwnerSayChatColor");
				}
				else if ( chat.mChatType == CHAT_TYPE_DIRECT )
				{
					r_color = LLUIColorTable::instance().getColor("DirectChatColor");
				}
				else if ( chat.mChatType == CHAT_TYPE_IM )
				{
					r_color = LLUIColorTable::instance().getColor("ObjectIMColor");
					// <FS:LO> FIRE-5889: Object IM's Not Triggering Growl Notifications
					std::string msg = chat.mFromName;
					std::string prefix = chat.mText.substr(0, 4);
					if(prefix == "/me " || prefix == "/me'")
					{
						msg = msg + chat.mText.substr(3);
					}
					else
					{
						msg = msg + ": " + chat.mText;
					}
					gGrowlManager->notify(chat.mFromName, msg, GROWL_IM_MESSAGE_TYPE);
					// </FS:LO>
				}
				else
				{
					r_color = LLUIColorTable::instance().getColor("ObjectChatColor");
				}
				break;
			default:
				r_color.setToWhite();
		}

		//Keyword alerts -KC
		if ((gAgentID != chat.mFromID || chat.mFromName == SYSTEM_FROM) && FSKeywords::getInstance()->chatContainsKeyword(chat, is_local))
		{
			std::string msg = chat.mFromName;
			std::string prefix = chat.mText.substr(0, 4);
			if(prefix == "/me " || prefix == "/me'")
			{
				msg = msg + chat.mText.substr(3);
			}
			else
			{
				msg = msg + ": " + chat.mText;
			}

			gGrowlManager->notify("Keyword Alert", msg, "Keyword Alert");

			static LLCachedControl<bool> sFSKeywordChangeColor(gSavedPerAccountSettings, "FSKeywordChangeColor");
			if (sFSKeywordChangeColor)
			{
				static LLCachedControl<LLColor4> sFSKeywordColor(gSavedPerAccountSettings, "FSKeywordColor");
				r_color = sFSKeywordColor;
			}
		}

		if (!chat.mPosAgent.isExactlyZero())
		{
			LLVector3 pos_agent = gAgent.getPositionAgent();
			F32 distance_squared = dist_vec_squared(pos_agent, chat.mPosAgent);
// <FS:CR> Aurora Sim
			//F32 dist_near_chat = gAgent.getNearChatRadius();
			//if (!avatarp || dist_vec_squared(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > say_distance_squared)
			F32 dist_near_chat = LLWorld::getInstance()->getSayDistance();
// </FS:CR> Aurora Sim
			if (distance_squared > dist_near_chat * dist_near_chat)
			{
				// diminish far-off chat
				r_color.mV[VALPHA] = 0.8f;
			}
		}
	}
}

//static
void LLViewerChat::getChatColor(const LLChat& chat, std::string& r_color_name, F32& r_color_alpha)
{
	if(chat.mMuted)
	{
		r_color_name = "LtGray";
	}
	else
	{
		switch(chat.mSourceType)
		{
			case CHAT_SOURCE_SYSTEM:
				r_color_name = "SystemChatColor";
				break;

			case CHAT_SOURCE_AGENT:
				if (chat.mFromID.isNull())
				{
					r_color_name = "SystemChatColor";
				}
				else
				{
					if(gAgentID == chat.mFromID)
					{
						r_color_name = "UserChatColor";
					}
					else
					{
						r_color_name = "AgentChatColor";
					}
				}
				break;

			case CHAT_SOURCE_OBJECT:
				if (chat.mChatType == CHAT_TYPE_DEBUG_MSG)
				{
					r_color_name = "ScriptErrorColor";
				}
				else if ( chat.mChatType == CHAT_TYPE_OWNER )
				{
					r_color_name = "llOwnerSayChatColor";
				}
				else if ( chat.mChatType == CHAT_TYPE_DIRECT )
				{
					r_color_name = "DirectChatColor";
				}
				else if ( chat.mChatType == CHAT_TYPE_IM )
				{
					r_color_name = "ObjectIMColor";
				}
				else
				{
					r_color_name = "ObjectChatColor";
				}
				break;
			default:
				r_color_name = "White";
		}

		if (!chat.mPosAgent.isExactlyZero())
		{
			LLVector3 pos_agent = gAgent.getPositionAgent();
			F32 distance_squared = dist_vec_squared(pos_agent, chat.mPosAgent);
// <FS:CR> Aurora som
			//F32 dist_near_chat = gAgent.getNearChatRadius();
			F32 dist_near_chat = LLWorld::getInstance()->getSayDistance();
// </FS:CR> Aurora sim
			if (distance_squared > dist_near_chat * dist_near_chat)
			{
				// diminish far-off chat
				r_color_alpha = 0.8f;
			}
			else
			{
				r_color_alpha = 1.0f;
			}
		}
	}

}

//static
LLFontGL* LLViewerChat::getChatFont()
{
	S32 font_size = gSavedSettings.getS32("ChatFontSize");
	LLFontGL* fontp = NULL;
	switch(font_size)
	{
		case 0:
			fontp = LLFontGL::getFontSansSerifSmall();
			break;
		default:
		case 1:
			fontp = LLFontGL::getFontSansSerif();
			break;
		case 2:
			fontp = LLFontGL::getFontSansSerifBig();
			break;
		case 3:
			fontp = LLFontGL::getFontSansSerifHuge();
			break;
	}

	return fontp;

}

//static
S32 LLViewerChat::getChatFontSize()
{
	return gSavedSettings.getS32("ChatFontSize");
}

//static
void LLViewerChat::formatChatMsg(const LLChat& chat, std::string& formated_msg)
{
	std::string tmpmsg = chat.mText;

	if(chat.mChatStyle == CHAT_STYLE_IRC)
	{
		formated_msg = chat.mFromName + tmpmsg.substr(3);
	}
	else
	{
		formated_msg = tmpmsg;
	}

}

//static
std::string LLViewerChat::getSenderSLURL(const LLChat& chat, const LLSD& args)
{
	switch (chat.mSourceType)
	{
	case CHAT_SOURCE_AGENT:
		return LLSLURL("agent", chat.mFromID, "about").getSLURLString();

	case CHAT_SOURCE_OBJECT:
		return getObjectImSLURL(chat, args);

	// <FS:Ansariel> Stop spamming the log when processing system messages
	case CHAT_SOURCE_SYSTEM:
		return LLStringUtil::null;
	// </FS:Ansariel>

	default:
		llwarns << "Getting SLURL for an unsupported sender type: " << chat.mSourceType << llendl;
	}

	return LLStringUtil::null;
}

//static
std::string LLViewerChat::getObjectImSLURL(const LLChat& chat, const LLSD& args)
{
	std::string url = LLSLURL("objectim", chat.mFromID, "").getSLURLString();
	url += "?name=" + chat.mFromName;
	url += "&owner=" + chat.mOwnerID.asString();

	std::string slurl = args["slurl"].asString();
	if (slurl.empty())
	{
		LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosAgent(chat.mPosAgent);
		if(region)
		{
			LLSLURL region_slurl(region->getName(), chat.mPosAgent);
			slurl = region_slurl.getLocationString();
		}
	}

	url += "&slurl=" + LLURI::escape(slurl);

	return url;
}

//static
boost::signals2::connection LLViewerChat::setFontChangedCallback(const font_change_signal_t::slot_type& cb)
{
	return sChatFontChangedSignal.connect(cb);
}

//static
void LLViewerChat::signalChatFontChanged()
{
	// Notify all observers that our font has changed
	sChatFontChangedSignal(getChatFont());
}
<|endoftext|>


<commit_before>/**
* \file AnnLogger.hpp
* \brief Create a ostream to the Ogre logger
* \author A. Brainville (Ybalrid)
*/
#ifndef ANN_LOGGER
#define ANN_LOGGER

#include "systemMacro.h"
//The debug output is opened by the AnnEngine class
#include "AnnEngine.hpp"
//We need the standard string format to be accessible
#include <string>
#include <iostream>
namespace Annwvyn
{
	///Open an output stream to the engine log
	class DLL AnnDebug : public std::ostream
	{
		///Nested buffer class. Write the stings to the engine log.
		class AnnDebugBuff : public std::stringbuf
		{
		public:
			///Construct an AnnDebug buffer
			AnnDebugBuff() {};

			///Will sync the buffer
			~AnnDebugBuff()
			{
				pubsync();
			};

			///Sync the buffer by performing an AnnEngine::log, clear it and return success.
			int sync() override
			{
				AnnEngine::log(str());
				str("");
				return 0;
			};
		};

	public:
		///Create an AnnDebug object that offer you a output stream to the AnnEngine logger
		///This permit you to write messages to the log using C++ style ostream
		/// example : AnnDebug() << "Player life is now " << playerLife;
		/// where playerLife is a variable. Everything that works with an std::ostream works here.
		AnnDebug();

		///Permit to log a static string via the debug stream
		/// \copydoc AnnEngine::AnnDebug()
		AnnDebug(const std::string& message);

		///Destroy the debug outputer object
		~AnnDebug();
	};
}

#endif<commit_msg>Move implementaiton of AnnDebug()'s nested stream class to the cpp file<commit_after>/**
* \file AnnLogger.hpp
* \brief Create a ostream to the Ogre logger
* \author A. Brainville (Ybalrid)
*/
#ifndef ANN_LOGGER
#define ANN_LOGGER

#include "systemMacro.h"
//The debug output is opened by the AnnEngine class
#include "AnnEngine.hpp"
//We need the standard string format to be accessible
#include <string>
#include <iostream>
namespace Annwvyn
{
	///Open an output stream to the engine log
	class DLL AnnDebug : public std::ostream
	{
		///Nested buffer class. Write the stings to the engine log.
		class AnnDebugBuff : public std::stringbuf
		{
		public:
			///Construct an AnnDebug buffer
			AnnDebugBuff() {};

			///Will sync the buffer
			~AnnDebugBuff();;

			///Sync the buffer by performing an AnnEngine::log, clear it and return success.
			int sync() override;;
		};

	public:
		///Create an AnnDebug object that offer you a output stream to the AnnEngine logger
		///This permit you to write messages to the log using C++ style ostream
		/// example : AnnDebug() << "Player life is now " << playerLife;
		/// where playerLife is a variable. Everything that works with an std::ostream works here.
		AnnDebug();

		///Permit to log a static string via the debug stream
		/// \copydoc AnnEngine::AnnDebug()
		AnnDebug(const std::string& message);

		///Destroy the debug outputer object
		~AnnDebug();
	};
}

#endif<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016 dresden elektronik ingenieurtechnik gmbh.
 * All rights reserved.
 *
 * The software in this package is published under the terms of the BSD
 * style license a copy of which has been included with this distribution in
 * the LICENSE.txt file.
 *
 */

#include <QApplication>
#include <QDesktopServices>
#include <QFile>
#include <QDir>
#include <QString>
#include <QProcess>
#include "de_web_plugin.h"
#include "de_web_plugin_private.h"

#define FW_IDLE_TIMEOUT (10 * 1000)
#define FW_WAIT_UPDATE_READY (2) //s
#define FW_IDLE_TIMEOUT_LONG (240 * 1000)
#define FW_WAIT_USER_TIMEOUT (120 * 1000)

/*! Inits the firmware update manager.
 */
void DeRestPluginPrivate::initFirmwareUpdate()
{
    fwProcess = 0;
    fwUpdateState = FW_Idle;

    Q_ASSERT(apsCtrl);
    apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateIdle);

    fwUpdateStartedByUser = false;
    fwUpdateTimer = new QTimer(this);
    fwUpdateTimer->setSingleShot(true);
    connect(fwUpdateTimer, SIGNAL(timeout()),
            this, SLOT(firmwareUpdateTimerFired()));
    fwUpdateTimer->start(5000);
}

/*! Starts the actual firmware update process.
 */
void DeRestPluginPrivate::updateFirmware()
{
    if (gwFirmwareNeedUpdate)
    {
        gwFirmwareNeedUpdate = false;
    }

    Q_ASSERT(apsCtrl);
    if (apsCtrl->getParameter(deCONZ::ParamFirmwareUpdateActive) == deCONZ::FirmwareUpdateIdle ||
        apsCtrl->getParameter(deCONZ::ParamDeviceConnected) == 1)
    {
        DBG_Printf(DBG_INFO, "GW firmware update conditions not met, abort\n");
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT);
        updateEtag(gwConfigEtag);
        return;
    }

    QString gcfFlasherBin = qApp->applicationDirPath() + "/GCFFlasher";
#ifdef Q_OS_WIN
    gcfFlasherBin.append(".exe");
    QString bin = gcfFlasherBin;
#elif defined(Q_OS_LINUX) && !defined(ARCH_ARM) // on RPi a normal sudo is ok since we don't need password there
    QString bin = "pkexec";
    gcfFlasherBin = "/usr/bin/GCFFlasher_internal";
    fwProcessArgs.prepend(gcfFlasherBin);
#elif defined(Q_OS_OSX)
    // TODO
    // /usr/bin/osascript -e 'do shell script "make install" with administrator privileges'
    QString bin = "sudo";
    fwProcessArgs.prepend(gcfFlasherBin);
#else
    QString bin = "sudo";
    gcfFlasherBin = "/usr/bin/GCFFlasher_internal";
    fwProcessArgs.prepend(gcfFlasherBin);
#endif

    if (!fwProcess)
    {
        fwProcess = new QProcess(this);
    }

    fwProcessArgs << "-f" << fwUpdateFile;

    fwUpdateState = FW_UpdateWaitFinished;
    updateEtag(gwConfigEtag);
    fwUpdateTimer->start(250);

    fwProcess->start(bin, fwProcessArgs);
}

/*! Observes the firmware update process.
 */
void DeRestPluginPrivate::updateFirmwareWaitFinished()
{
    if (fwProcess)
    {
        if (fwProcess->bytesAvailable())
        {
            QByteArray data = fwProcess->readAllStandardOutput();
            DBG_Printf(DBG_INFO, "%s", qPrintable(data));

            if (apsCtrl->getParameter(deCONZ::ParamFirmwareUpdateActive) != deCONZ::FirmwareUpdateRunning)
            {
                if (data.contains("flashing"))
                {
                    apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateRunning);
                }
            }
        }

        if (fwProcess->state() == QProcess::Starting)
        {
            DBG_Printf(DBG_INFO, "GW firmware update starting ..\n");
        }
        else if (fwProcess->state() == QProcess::Running)
        {
            DBG_Printf(DBG_INFO_L2, "GW firmware update running ..\n");
        }
        else if (fwProcess->state() == QProcess::NotRunning)
        {
            if (fwProcess->exitStatus() == QProcess::NormalExit)
            {
                DBG_Printf(DBG_INFO, "GW firmware update exit code %d\n", fwProcess->exitCode());
            }
            else if (fwProcess->exitStatus() == QProcess::CrashExit)
            {
                DBG_Printf(DBG_INFO, "GW firmware update crashed %s\n", qPrintable(fwProcess->errorString()));
            }

            fwProcess->deleteLater();
            fwProcess = 0;
        }
    }

    // done
    if (fwProcess == 0)
    {
        fwUpdateStartedByUser = false;
        gwFirmwareNeedUpdate = false;
        updateEtag(gwConfigEtag);
        apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateIdle);
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT_LONG);
    }
    else // recheck
    {
        fwUpdateTimer->start(250);
    }
}

/*! Starts the device disconnect so that the serial port is released.
 */
void DeRestPluginPrivate::updateFirmwareDisconnectDevice()
{
    Q_ASSERT(apsCtrl);
//    if (apsCtrl->getParameter(deCONZ::ParamFirmwareUpdateActive) == deCONZ::FirmwareUpdateIdle)
//    {
//        if (apsCtrl->getParameter(deCONZ::ParamDeviceConnected) == 1)
//        {
//            DBG_Printf(DBG_INFO, "GW firmware disconnect device before update\n");
//        }
//    }

    if (apsCtrl->getParameter(deCONZ::ParamDeviceConnected) == 1)
    {
        fwUpdateTimer->start(100); // recheck
    }
    else
    {
        DBG_Printf(DBG_INFO, "GW firmware start update (device not connected)\n");
        fwUpdateState = FW_Update;
        fwUpdateTimer->start(0);
        updateEtag(gwConfigEtag);
    }
}

/*! Starts the firmware update.
 */
bool DeRestPluginPrivate::startUpdateFirmware()
{
    fwUpdateStartedByUser = true;
    if (fwUpdateState == FW_WaitUserConfirm)
    {
        apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateRunning);
        updateEtag(gwConfigEtag);
        fwUpdateState = FW_DisconnectDevice;
        fwUpdateTimer->start(100);
        return true;
    }

    return false;
}

/*! Delayed trigger to update the firmware.
 */
void DeRestPluginPrivate::firmwareUpdateTimerFired()
{
    if (fwUpdateState == FW_Idle)
    {
        if (gwFirmwareNeedUpdate)
        {
            gwFirmwareNeedUpdate = false;
            updateEtag(gwConfigEtag);
        }
        fwUpdateState = FW_CheckDevices;
        fwUpdateTimer->start(0);
    }
    else if (fwUpdateState == FW_CheckDevices)
    {
        checkFirmwareDevices();
    }
    else if (fwUpdateState == FW_CheckVersion)
    {
        queryFirmwareVersion();
    }
    else if (fwUpdateState == FW_DisconnectDevice)
    {
        updateFirmwareDisconnectDevice();
    }
    else if (fwUpdateState == FW_Update)
    {
        updateFirmware();
    }
    else if (fwUpdateState == FW_UpdateWaitFinished)
    {
        updateFirmwareWaitFinished();
    }
    else if (fwUpdateState == FW_WaitUserConfirm)
    {
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT);
    }
    else
    {
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT);
    }
}

/*! Lazy query of firmware version.
    Because the device might not be connected at first optaining the
    firmware version must be delayed.

    If the firmware is older then the min required firmware for the platform
    and a proper firmware update file exists, the API will announce that a
    firmware update is available.
 */
void DeRestPluginPrivate::queryFirmwareVersion()
{
    Q_ASSERT(apsCtrl);
    if (!apsCtrl)
    {
        return;
    }

    { // check for GCFFlasher binary
        QString gcfFlasherBin = qApp->applicationDirPath() + "/GCFFlasher";
#ifdef Q_OS_WIN
        gcfFlasherBin.append(".exe");
#elif defined(Q_OS_LINUX) && !defined(ARCH_ARM) // on RPi a normal sudo is ok since we don't need password there
        gcfFlasherBin = "/usr/bin/GCFFlasher_internal";
#elif defined(Q_OS_OSX)
        // TODO
#else
        gcfFlasherBin = "/usr/bin/GCFFlasher_internal";
#endif

        if (!QFile::exists(gcfFlasherBin))
        {
            DBG_Printf(DBG_INFO, "GW update firmware failed, %s doesn't exist\n", qPrintable(gcfFlasherBin));
            fwUpdateState = FW_Idle;
            fwUpdateTimer->start(FW_IDLE_TIMEOUT_LONG);
            return;
        }
    }

    // does the update file exist?
    if (fwUpdateFile.isEmpty())
    {
        QString fileName;
        fileName.sprintf("deCONZ_Rpi_0x%08x.bin.GCF", GW_MIN_RPI_FW_VERSION);

        // search in different locations
        std::vector<QString> paths;
#ifdef Q_OS_LINUX
        paths.push_back(QLatin1String("/usr/share/deCONZ/firmware/"));
#endif
        paths.push_back(deCONZ::getStorageLocation(deCONZ::ApplicationsDataLocation) + QLatin1String("/firmware/"));
        paths.push_back(deCONZ::getStorageLocation(deCONZ::HomeLocation) + QLatin1String("/raspbee_firmware/"));
#ifdef Q_OS_OSX
        QDir dir(qApp->applicationDirPath());
        dir.cdUp();
        dir.cd("Resources");
        paths.push_back(dir.path() + "/");
#endif

        std::vector<QString>::const_iterator i = paths.begin();
        std::vector<QString>::const_iterator end = paths.end();
        for (; i != end; ++i)
        {
            if (QFile::exists(*i + fileName))
            {
                fwUpdateFile = *i + fileName;
                DBG_Printf(DBG_INFO, "GW update firmware found: %s\n", qPrintable(fwUpdateFile));
                break;
            }
        }
    }

    if (fwUpdateFile.isEmpty())
    {
        DBG_Printf(DBG_ERROR, "GW update firmware not found: %s\n", qPrintable(fwUpdateFile));
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT);
        return;
    }

    uint8_t devConnected = apsCtrl->getParameter(deCONZ::ParamDeviceConnected);
    uint32_t fwVersion = apsCtrl->getParameter(deCONZ::ParamFirmwareVersion);

    Q_ASSERT(!gwFirmwareNeedUpdate);

    if (devConnected == 0 || fwVersion == 0)
    {
        // if even after some time no firmware was detected
        // ASSUME that a device is present and reachable but might not have firmware installed
//        if (getUptime() >= FW_WAIT_UPDATE_READY)
        {
            QString str;
            str.sprintf("0x%08x", GW_MIN_RPI_FW_VERSION);

            gwFirmwareVersion = "0x00000000"; // unknown
            gwFirmwareVersionUpdate = str;
            gwConfig["fwversion"] = gwFirmwareVersion;
            gwFirmwareNeedUpdate = true;
            updateEtag(gwConfigEtag);

            fwUpdateState = FW_WaitUserConfirm;
            fwUpdateTimer->start(FW_WAIT_USER_TIMEOUT);
            apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateReadyToStart);

            if (fwUpdateStartedByUser)
            {
                startUpdateFirmware();
            }
        }
        return;
    }
    else if (devConnected)
    {
        QString str;
        str.sprintf("0x%08x", fwVersion);

        if (gwFirmwareVersion != str)
        {
            gwFirmwareVersion = str;
            gwConfig["fwversion"] = str;
            updateEtag(gwConfigEtag);
        }

        DBG_Printf(DBG_INFO, "GW firmware version: %s\n", qPrintable(gwFirmwareVersion));

        // if the device is detected check that the firmware version is >= min version
        if ((fwVersion & FW_PLATFORM_MASK) == FW_PLATFORM_RPI)
        {
            if (fwVersion < GW_MIN_RPI_FW_VERSION)
            {
                gwFirmwareVersionUpdate.sprintf("0x%08x", GW_MIN_RPI_FW_VERSION);
                gwFirmwareNeedUpdate = true;
                updateEtag(gwConfigEtag);

                DBG_Printf(DBG_INFO, "GW firmware version shall be updated to: 0x%08x\n", GW_MIN_RPI_FW_VERSION);
                fwUpdateState = FW_WaitUserConfirm;
                fwUpdateTimer->start(FW_WAIT_USER_TIMEOUT);
                apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateReadyToStart);
                return;
            }
            else
            {
                DBG_Printf(DBG_INFO, "GW firmware version is up to date: 0x%08x\n", fwVersion);
                fwUpdateState = FW_Idle;
                fwUpdateTimer->start(FW_IDLE_TIMEOUT_LONG);
                return;
            }
        }

        if (!gwFirmwareVersionUpdate.isEmpty())
        {
            gwFirmwareVersionUpdate.clear();
            updateEtag(gwConfigEtag);
        }
    }

    fwUpdateState = FW_Idle;
    fwUpdateTimer->start(FW_IDLE_TIMEOUT);
}

/*! Checks if devices for firmware update are present.
 */
void DeRestPluginPrivate::checkFirmwareDevices()
{
    deCONZ::DeviceEnumerator devEnumerator;

    fwProcessArgs.clear();

    devEnumerator.listSerialPorts();
    const std::vector<deCONZ::DeviceEntry> &availPorts = devEnumerator.getList();

    std::vector<deCONZ::DeviceEntry>::const_iterator i = availPorts.begin();
    std::vector<deCONZ::DeviceEntry>::const_iterator end = availPorts.end();

    int raspBeeCount = 0;
    int usbDongleCount = 0;
    QString ttyPath;

    for (; i != end; ++i)
    {
        if (i->friendlyName.contains(QLatin1String("ConBee")))
        {
            usbDongleCount++;
        }
        else if (i->friendlyName.contains(QLatin1String("RaspBee")))
        {
            raspBeeCount = 1;
            ttyPath = i->path;
        }
    }

    if (usbDongleCount > 1)
    {
        DBG_Printf(DBG_INFO_L2, "GW firmware update too many USB devices connected, abort\n");
    }
    else if (usbDongleCount == 1)
    {
        DBG_Printf(DBG_INFO_L2, "GW firmware update select USB device\n");
        fwProcessArgs << "-d" << "0";
    }
    else if (raspBeeCount > 0 && usbDongleCount == 0 && !ttyPath.isEmpty())
    {
        DBG_Printf(DBG_INFO_L2, "GW firmware update select %s device\n", qPrintable(ttyPath));
        fwProcessArgs << "-d" << "RaspBee";
    }

    if (!fwProcessArgs.isEmpty())
    {
        fwUpdateState = FW_CheckVersion;
        fwUpdateTimer->start(0);
        return;
    }

    fwUpdateState = FW_Idle;
    fwUpdateTimer->start(FW_IDLE_TIMEOUT);
}
<commit_msg>Don't check firmware while OTA is busy<commit_after>/*
 * Copyright (c) 2016 dresden elektronik ingenieurtechnik gmbh.
 * All rights reserved.
 *
 * The software in this package is published under the terms of the BSD
 * style license a copy of which has been included with this distribution in
 * the LICENSE.txt file.
 *
 */

#include <QApplication>
#include <QDesktopServices>
#include <QFile>
#include <QDir>
#include <QString>
#include <QProcess>
#include "de_web_plugin.h"
#include "de_web_plugin_private.h"

#define FW_IDLE_TIMEOUT (10 * 1000)
#define FW_WAIT_UPDATE_READY (2) //s
#define FW_IDLE_TIMEOUT_LONG (240 * 1000)
#define FW_WAIT_USER_TIMEOUT (120 * 1000)

/*! Inits the firmware update manager.
 */
void DeRestPluginPrivate::initFirmwareUpdate()
{
    fwProcess = 0;
    fwUpdateState = FW_Idle;

    Q_ASSERT(apsCtrl);
    apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateIdle);

    fwUpdateStartedByUser = false;
    fwUpdateTimer = new QTimer(this);
    fwUpdateTimer->setSingleShot(true);
    connect(fwUpdateTimer, SIGNAL(timeout()),
            this, SLOT(firmwareUpdateTimerFired()));
    fwUpdateTimer->start(5000);
}

/*! Starts the actual firmware update process.
 */
void DeRestPluginPrivate::updateFirmware()
{
    if (gwFirmwareNeedUpdate)
    {
        gwFirmwareNeedUpdate = false;
    }

    Q_ASSERT(apsCtrl);
    if (apsCtrl->getParameter(deCONZ::ParamFirmwareUpdateActive) == deCONZ::FirmwareUpdateIdle ||
        apsCtrl->getParameter(deCONZ::ParamDeviceConnected) == 1)
    {
        DBG_Printf(DBG_INFO, "GW firmware update conditions not met, abort\n");
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT);
        updateEtag(gwConfigEtag);
        return;
    }

    QString gcfFlasherBin = qApp->applicationDirPath() + "/GCFFlasher";
#ifdef Q_OS_WIN
    gcfFlasherBin.append(".exe");
    QString bin = gcfFlasherBin;
#elif defined(Q_OS_LINUX) && !defined(ARCH_ARM) // on RPi a normal sudo is ok since we don't need password there
    QString bin = "pkexec";
    gcfFlasherBin = "/usr/bin/GCFFlasher_internal";
    fwProcessArgs.prepend(gcfFlasherBin);
#elif defined(Q_OS_OSX)
    // TODO
    // /usr/bin/osascript -e 'do shell script "make install" with administrator privileges'
    QString bin = "sudo";
    fwProcessArgs.prepend(gcfFlasherBin);
#else
    QString bin = "sudo";
    gcfFlasherBin = "/usr/bin/GCFFlasher_internal";
    fwProcessArgs.prepend(gcfFlasherBin);
#endif

    if (!fwProcess)
    {
        fwProcess = new QProcess(this);
    }

    fwProcessArgs << "-f" << fwUpdateFile;

    fwUpdateState = FW_UpdateWaitFinished;
    updateEtag(gwConfigEtag);
    fwUpdateTimer->start(250);

    fwProcess->start(bin, fwProcessArgs);
}

/*! Observes the firmware update process.
 */
void DeRestPluginPrivate::updateFirmwareWaitFinished()
{
    if (fwProcess)
    {
        if (fwProcess->bytesAvailable())
        {
            QByteArray data = fwProcess->readAllStandardOutput();
            DBG_Printf(DBG_INFO, "%s", qPrintable(data));

            if (apsCtrl->getParameter(deCONZ::ParamFirmwareUpdateActive) != deCONZ::FirmwareUpdateRunning)
            {
                if (data.contains("flashing"))
                {
                    apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateRunning);
                }
            }
        }

        if (fwProcess->state() == QProcess::Starting)
        {
            DBG_Printf(DBG_INFO, "GW firmware update starting ..\n");
        }
        else if (fwProcess->state() == QProcess::Running)
        {
            DBG_Printf(DBG_INFO_L2, "GW firmware update running ..\n");
        }
        else if (fwProcess->state() == QProcess::NotRunning)
        {
            if (fwProcess->exitStatus() == QProcess::NormalExit)
            {
                DBG_Printf(DBG_INFO, "GW firmware update exit code %d\n", fwProcess->exitCode());
            }
            else if (fwProcess->exitStatus() == QProcess::CrashExit)
            {
                DBG_Printf(DBG_INFO, "GW firmware update crashed %s\n", qPrintable(fwProcess->errorString()));
            }

            fwProcess->deleteLater();
            fwProcess = 0;
        }
    }

    // done
    if (fwProcess == 0)
    {
        fwUpdateStartedByUser = false;
        gwFirmwareNeedUpdate = false;
        updateEtag(gwConfigEtag);
        apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateIdle);
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT_LONG);
    }
    else // recheck
    {
        fwUpdateTimer->start(250);
    }
}

/*! Starts the device disconnect so that the serial port is released.
 */
void DeRestPluginPrivate::updateFirmwareDisconnectDevice()
{
    Q_ASSERT(apsCtrl);
//    if (apsCtrl->getParameter(deCONZ::ParamFirmwareUpdateActive) == deCONZ::FirmwareUpdateIdle)
//    {
//        if (apsCtrl->getParameter(deCONZ::ParamDeviceConnected) == 1)
//        {
//            DBG_Printf(DBG_INFO, "GW firmware disconnect device before update\n");
//        }
//    }

    if (apsCtrl->getParameter(deCONZ::ParamDeviceConnected) == 1)
    {
        fwUpdateTimer->start(100); // recheck
    }
    else
    {
        DBG_Printf(DBG_INFO, "GW firmware start update (device not connected)\n");
        fwUpdateState = FW_Update;
        fwUpdateTimer->start(0);
        updateEtag(gwConfigEtag);
    }
}

/*! Starts the firmware update.
 */
bool DeRestPluginPrivate::startUpdateFirmware()
{
    fwUpdateStartedByUser = true;
    if (fwUpdateState == FW_WaitUserConfirm)
    {
        apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateRunning);
        updateEtag(gwConfigEtag);
        fwUpdateState = FW_DisconnectDevice;
        fwUpdateTimer->start(100);
        return true;
    }

    return false;
}

/*! Delayed trigger to update the firmware.
 */
void DeRestPluginPrivate::firmwareUpdateTimerFired()
{
    if (d->otauLastBusyTimeDelta() < OTA_LOW_PRIORITY_TIME)
    {
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT);
    }
    else if (fwUpdateState == FW_Idle)
    {
        if (gwFirmwareNeedUpdate)
        {
            gwFirmwareNeedUpdate = false;
            updateEtag(gwConfigEtag);
        }
        fwUpdateState = FW_CheckDevices;
        fwUpdateTimer->start(0);
    }
    else if (fwUpdateState == FW_CheckDevices)
    {
        checkFirmwareDevices();
    }
    else if (fwUpdateState == FW_CheckVersion)
    {
        queryFirmwareVersion();
    }
    else if (fwUpdateState == FW_DisconnectDevice)
    {
        updateFirmwareDisconnectDevice();
    }
    else if (fwUpdateState == FW_Update)
    {
        updateFirmware();
    }
    else if (fwUpdateState == FW_UpdateWaitFinished)
    {
        updateFirmwareWaitFinished();
    }
    else if (fwUpdateState == FW_WaitUserConfirm)
    {
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT);
    }
    else
    {
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT);
    }
}

/*! Lazy query of firmware version.
    Because the device might not be connected at first optaining the
    firmware version must be delayed.

    If the firmware is older then the min required firmware for the platform
    and a proper firmware update file exists, the API will announce that a
    firmware update is available.
 */
void DeRestPluginPrivate::queryFirmwareVersion()
{
    Q_ASSERT(apsCtrl);
    if (!apsCtrl)
    {
        return;
    }

    { // check for GCFFlasher binary
        QString gcfFlasherBin = qApp->applicationDirPath() + "/GCFFlasher";
#ifdef Q_OS_WIN
        gcfFlasherBin.append(".exe");
#elif defined(Q_OS_LINUX) && !defined(ARCH_ARM) // on RPi a normal sudo is ok since we don't need password there
        gcfFlasherBin = "/usr/bin/GCFFlasher_internal";
#elif defined(Q_OS_OSX)
        // TODO
#else
        gcfFlasherBin = "/usr/bin/GCFFlasher_internal";
#endif

        if (!QFile::exists(gcfFlasherBin))
        {
            DBG_Printf(DBG_INFO, "GW update firmware failed, %s doesn't exist\n", qPrintable(gcfFlasherBin));
            fwUpdateState = FW_Idle;
            fwUpdateTimer->start(FW_IDLE_TIMEOUT_LONG);
            return;
        }
    }

    // does the update file exist?
    if (fwUpdateFile.isEmpty())
    {
        QString fileName;
        fileName.sprintf("deCONZ_Rpi_0x%08x.bin.GCF", GW_MIN_RPI_FW_VERSION);

        // search in different locations
        std::vector<QString> paths;
#ifdef Q_OS_LINUX
        paths.push_back(QLatin1String("/usr/share/deCONZ/firmware/"));
#endif
        paths.push_back(deCONZ::getStorageLocation(deCONZ::ApplicationsDataLocation) + QLatin1String("/firmware/"));
        paths.push_back(deCONZ::getStorageLocation(deCONZ::HomeLocation) + QLatin1String("/raspbee_firmware/"));
#ifdef Q_OS_OSX
        QDir dir(qApp->applicationDirPath());
        dir.cdUp();
        dir.cd("Resources");
        paths.push_back(dir.path() + "/");
#endif

        std::vector<QString>::const_iterator i = paths.begin();
        std::vector<QString>::const_iterator end = paths.end();
        for (; i != end; ++i)
        {
            if (QFile::exists(*i + fileName))
            {
                fwUpdateFile = *i + fileName;
                DBG_Printf(DBG_INFO, "GW update firmware found: %s\n", qPrintable(fwUpdateFile));
                break;
            }
        }
    }

    if (fwUpdateFile.isEmpty())
    {
        DBG_Printf(DBG_ERROR, "GW update firmware not found: %s\n", qPrintable(fwUpdateFile));
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT);
        return;
    }

    uint8_t devConnected = apsCtrl->getParameter(deCONZ::ParamDeviceConnected);
    uint32_t fwVersion = apsCtrl->getParameter(deCONZ::ParamFirmwareVersion);

    Q_ASSERT(!gwFirmwareNeedUpdate);

    if (devConnected == 0 || fwVersion == 0)
    {
        // if even after some time no firmware was detected
        // ASSUME that a device is present and reachable but might not have firmware installed
//        if (getUptime() >= FW_WAIT_UPDATE_READY)
        {
            QString str;
            str.sprintf("0x%08x", GW_MIN_RPI_FW_VERSION);

            gwFirmwareVersion = "0x00000000"; // unknown
            gwFirmwareVersionUpdate = str;
            gwConfig["fwversion"] = gwFirmwareVersion;
            gwFirmwareNeedUpdate = true;
            updateEtag(gwConfigEtag);

            fwUpdateState = FW_WaitUserConfirm;
            fwUpdateTimer->start(FW_WAIT_USER_TIMEOUT);
            apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateReadyToStart);

            if (fwUpdateStartedByUser)
            {
                startUpdateFirmware();
            }
        }
        return;
    }
    else if (devConnected)
    {
        QString str;
        str.sprintf("0x%08x", fwVersion);

        if (gwFirmwareVersion != str)
        {
            gwFirmwareVersion = str;
            gwConfig["fwversion"] = str;
            updateEtag(gwConfigEtag);
        }

        DBG_Printf(DBG_INFO, "GW firmware version: %s\n", qPrintable(gwFirmwareVersion));

        // if the device is detected check that the firmware version is >= min version
        if ((fwVersion & FW_PLATFORM_MASK) == FW_PLATFORM_RPI)
        {
            if (fwVersion < GW_MIN_RPI_FW_VERSION)
            {
                gwFirmwareVersionUpdate.sprintf("0x%08x", GW_MIN_RPI_FW_VERSION);
                gwFirmwareNeedUpdate = true;
                updateEtag(gwConfigEtag);

                DBG_Printf(DBG_INFO, "GW firmware version shall be updated to: 0x%08x\n", GW_MIN_RPI_FW_VERSION);
                fwUpdateState = FW_WaitUserConfirm;
                fwUpdateTimer->start(FW_WAIT_USER_TIMEOUT);
                apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateReadyToStart);
                return;
            }
            else
            {
                DBG_Printf(DBG_INFO, "GW firmware version is up to date: 0x%08x\n", fwVersion);
                fwUpdateState = FW_Idle;
                fwUpdateTimer->start(FW_IDLE_TIMEOUT_LONG);
                return;
            }
        }

        if (!gwFirmwareVersionUpdate.isEmpty())
        {
            gwFirmwareVersionUpdate.clear();
            updateEtag(gwConfigEtag);
        }
    }

    fwUpdateState = FW_Idle;
    fwUpdateTimer->start(FW_IDLE_TIMEOUT);
}

/*! Checks if devices for firmware update are present.
 */
void DeRestPluginPrivate::checkFirmwareDevices()
{
    deCONZ::DeviceEnumerator devEnumerator;

    fwProcessArgs.clear();

    devEnumerator.listSerialPorts();
    const std::vector<deCONZ::DeviceEntry> &availPorts = devEnumerator.getList();

    std::vector<deCONZ::DeviceEntry>::const_iterator i = availPorts.begin();
    std::vector<deCONZ::DeviceEntry>::const_iterator end = availPorts.end();

    int raspBeeCount = 0;
    int usbDongleCount = 0;
    QString ttyPath;

    for (; i != end; ++i)
    {
        if (i->friendlyName.contains(QLatin1String("ConBee")))
        {
            usbDongleCount++;
        }
        else if (i->friendlyName.contains(QLatin1String("RaspBee")))
        {
            raspBeeCount = 1;
            ttyPath = i->path;
        }
    }

    if (usbDongleCount > 1)
    {
        DBG_Printf(DBG_INFO_L2, "GW firmware update too many USB devices connected, abort\n");
    }
    else if (usbDongleCount == 1)
    {
        DBG_Printf(DBG_INFO_L2, "GW firmware update select USB device\n");
        fwProcessArgs << "-d" << "0";
    }
    else if (raspBeeCount > 0 && usbDongleCount == 0 && !ttyPath.isEmpty())
    {
        DBG_Printf(DBG_INFO_L2, "GW firmware update select %s device\n", qPrintable(ttyPath));
        fwProcessArgs << "-d" << "RaspBee";
    }

    if (!fwProcessArgs.isEmpty())
    {
        fwUpdateState = FW_CheckVersion;
        fwUpdateTimer->start(0);
        return;
    }

    fwUpdateState = FW_Idle;
    fwUpdateTimer->start(FW_IDLE_TIMEOUT);
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016 dresden elektronik ingenieurtechnik gmbh.
 * All rights reserved.
 *
 * The software in this package is published under the terms of the BSD
 * style license a copy of which has been included with this distribution in
 * the LICENSE.txt file.
 *
 */

#include <QApplication>
#include <QDesktopServices>
#include <QFile>
#include <QDir>
#include <QString>
#include <QProcess>
#include "de_web_plugin.h"
#include "de_web_plugin_private.h"

#define FW_IDLE_TIMEOUT (10 * 1000)
#define FW_WAIT_UPDATE_READY (2) //s
#define FW_IDLE_TIMEOUT_LONG (240 * 1000)
#define FW_WAIT_USER_TIMEOUT (120 * 1000)

/*! Inits the firmware update manager.
 */
void DeRestPluginPrivate::initFirmwareUpdate()
{
    fwProcess = 0;
    fwUpdateState = FW_Idle;

    Q_ASSERT(apsCtrl);
    apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateIdle);

    fwUpdateStartedByUser = false;
    fwUpdateTimer = new QTimer(this);
    fwUpdateTimer->setSingleShot(true);
    connect(fwUpdateTimer, SIGNAL(timeout()),
            this, SLOT(firmwareUpdateTimerFired()));
    fwUpdateTimer->start(1000);
}

/*! Starts the actual firmware update process.
 */
void DeRestPluginPrivate::updateFirmware()
{
    if (gwFirmwareNeedUpdate)
    {
        gwFirmwareNeedUpdate = false;
    }

    Q_ASSERT(apsCtrl);
    if (apsCtrl->getParameter(deCONZ::ParamFirmwareUpdateActive) == deCONZ::FirmwareUpdateIdle ||
        apsCtrl->getParameter(deCONZ::ParamDeviceConnected) == 1)
    {
        DBG_Printf(DBG_INFO, "GW firmware update conditions not met, abort\n");
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT);
        return;
    }

    QString gcfFlasherBin = qApp->applicationDirPath() + "/GCFFlasher";
#ifdef Q_OS_WIN
    gcfFlasherBin.append(".exe");
    QString bin = gcfFlasherBin;
#elif defined(Q_OS_LINUX) && !defined(ARCH_ARM) // on RPi a normal sudo is ok since we don't need password there
    QString bin = "pkexec";
    gcfFlasherBin = "/usr/bin/GCFFlasher";
    fwProcessArgs.prepend(gcfFlasherBin);
#elif defined(Q_OS_OSX)
    // TODO
    // /usr/bin/osascript -e 'do shell script "make install" with administrator privileges'
    QString bin = "sudo";
    fwProcessArgs.prepend(gcfFlasherBin);
#else
    QString bin = "sudo";
    fwProcessArgs.prepend(gcfFlasherBin);
#endif

    if (!fwProcess)
    {
        fwProcess = new QProcess(this);
    }

    fwProcessArgs << "-f" << fwUpdateFile;

    fwUpdateState = FW_UpdateWaitFinished;
    fwUpdateTimer->start(250);

    fwProcess->start(bin, fwProcessArgs);
}

/*! Observes the firmware update process.
 */
void DeRestPluginPrivate::updateFirmwareWaitFinished()
{
    if (fwProcess)
    {
        if (fwProcess->bytesAvailable())
        {
            QByteArray data = fwProcess->readAllStandardOutput();
            DBG_Printf(DBG_INFO, "%s", qPrintable(data));

            if (apsCtrl->getParameter(deCONZ::ParamFirmwareUpdateActive) != deCONZ::FirmwareUpdateRunning)
            {
                if (data.contains("flashing"))
                {
                    apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateRunning);
                }
            }
        }

        if (fwProcess->state() == QProcess::Starting)
        {
            DBG_Printf(DBG_INFO, "GW firmware update starting ..\n");
        }
        else if (fwProcess->state() == QProcess::Running)
        {
            DBG_Printf(DBG_INFO_L2, "GW firmware update running ..\n");
        }
        else if (fwProcess->state() == QProcess::NotRunning)
        {
            if (fwProcess->exitStatus() == QProcess::NormalExit)
            {
                DBG_Printf(DBG_INFO, "GW firmware update exit code %d\n", fwProcess->exitCode());
            }
            else if (fwProcess->exitStatus() == QProcess::CrashExit)
            {
                DBG_Printf(DBG_INFO, "GW firmware update crashed %s\n", qPrintable(fwProcess->errorString()));
            }

            fwProcess->deleteLater();
            fwProcess = 0;
        }
    }

    // done
    if (fwProcess == 0)
    {
        fwUpdateStartedByUser = false;
        apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateIdle);
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT);
    }
    else // recheck
    {
        fwUpdateTimer->start(250);
    }
}

/*! Starts the device disconnect so that the serial port is released.
 */
void DeRestPluginPrivate::updateFirmwareDisconnectDevice()
{
    Q_ASSERT(apsCtrl);
//    if (apsCtrl->getParameter(deCONZ::ParamFirmwareUpdateActive) == deCONZ::FirmwareUpdateIdle)
//    {
//        if (apsCtrl->getParameter(deCONZ::ParamDeviceConnected) == 1)
//        {
//            DBG_Printf(DBG_INFO, "GW firmware disconnect device before update\n");
//        }
//    }

    if (apsCtrl->getParameter(deCONZ::ParamDeviceConnected) == 1)
    {
        fwUpdateTimer->start(100); // recheck
    }
    else
    {
        DBG_Printf(DBG_INFO, "GW firmware start update (device not connected)\n");
        fwUpdateState = FW_Update;
        fwUpdateTimer->start(0);
    }
}

/*! Starts the firmware update.
 */
bool DeRestPluginPrivate::startUpdateFirmware()
{
    fwUpdateStartedByUser = true;
    if (fwUpdateState == FW_WaitUserConfirm)
    {
        fwUpdateState = FW_DisconnectDevice;
        fwUpdateTimer->start(100);
        return true;
    }

    return false;
}

/*! Delayed trigger to update the firmware.
 */
void DeRestPluginPrivate::firmwareUpdateTimerFired()
{
    if (fwUpdateState == FW_Idle)
    {
        if (gwFirmwareNeedUpdate)
        {
            gwFirmwareNeedUpdate = false;
            updateEtag(gwConfigEtag);
        }
        fwUpdateState = FW_CheckDevices;
        fwUpdateTimer->start(0);
    }
    else if (fwUpdateState == FW_CheckDevices)
    {
        checkFirmwareDevices();
    }
    else if (fwUpdateState == FW_CheckVersion)
    {
        queryFirmwareVersion();
    }
    else if (fwUpdateState == FW_DisconnectDevice)
    {
        updateFirmwareDisconnectDevice();
    }
    else if (fwUpdateState == FW_Update)
    {
        updateFirmware();
    }
    else if (fwUpdateState == FW_UpdateWaitFinished)
    {
        updateFirmwareWaitFinished();
    }
    else if (fwUpdateState == FW_WaitUserConfirm)
    {
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT);
    }
    else
    {
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT);
    }
}

/*! Lazy query of firmware version.
    Because the device might not be connected at first optaining the
    firmware version must be delayed.

    If the firmware is older then the min required firmware for the platform
    and a proper firmware update file exists, the API will announce that a
    firmware update is available.
 */
void DeRestPluginPrivate::queryFirmwareVersion()
{
    Q_ASSERT(apsCtrl);
    if (!apsCtrl)
    {
        return;
    }

    { // check for GCFFlasher binary
        QString gcfFlasherBin = qApp->applicationDirPath() + "/GCFFlasher";
#ifdef Q_OS_WIN
        gcfFlasherBin.append(".exe");
#elif defined(Q_OS_LINUX)
        gcfFlasherBin = "/usr/bin/GCFFlasher";
#endif

        if (!QFile::exists(gcfFlasherBin))
        {
            DBG_Printf(DBG_INFO, "GW update firmware failed, %s doesn't exist\n", qPrintable(gcfFlasherBin));
            fwUpdateState = FW_Idle;
            fwUpdateTimer->start(FW_IDLE_TIMEOUT_LONG);
            return;
        }
    }

    // does the update file exist?
    if (fwUpdateFile.isEmpty())
    {
        QString fileName;
        fileName.sprintf("deCONZ_Rpi_0x%08x.bin.GCF", GW_MIN_RPI_FW_VERSION);

        // search in different locations
        std::vector<QString> paths;
        paths.push_back(deCONZ::getStorageLocation(deCONZ::ApplicationsDataLocation) + QLatin1String("/firmware/"));
        paths.push_back(deCONZ::getStorageLocation(deCONZ::HomeLocation) + QLatin1String("/raspbee_firmware/"));
#ifdef Q_OS_OSX
        QDir dir(qApp->applicationDirPath());
        dir.cdUp();
        dir.cd("Resources");
        paths.push_back(dir.path() + "/");
#endif

        std::vector<QString>::const_iterator i = paths.begin();
        std::vector<QString>::const_iterator end = paths.end();
        for (; i != end; ++i)
        {
            if (QFile::exists(*i + fileName))
            {
                fwUpdateFile = *i + fileName;
                DBG_Printf(DBG_INFO, "GW update firmware found: %s\n", qPrintable(fwUpdateFile));
                break;
            }
        }
    }

    if (fwUpdateFile.isEmpty())
    {
        DBG_Printf(DBG_ERROR, "GW update firmware not found: %s\n", qPrintable(fwUpdateFile));
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT);
        return;
    }

    uint8_t devConnected = apsCtrl->getParameter(deCONZ::ParamDeviceConnected);
    uint32_t fwVersion = apsCtrl->getParameter(deCONZ::ParamFirmwareVersion);

    Q_ASSERT(!gwFirmwareNeedUpdate);

    if (devConnected == 0 || fwVersion == 0)
    {
        // if even after some time no firmware was detected
        // ASSUME that a device is present and reachable but might not have firmware installed
//        if (getUptime() >= FW_WAIT_UPDATE_READY)
        {
            QString str;
            str.sprintf("0x%08x", GW_MIN_RPI_FW_VERSION);

            gwFirmwareVersion = "0x00000000"; // unknown
            gwFirmwareVersionUpdate = str;
            gwConfig["fwversion"] = gwFirmwareVersion;
            gwFirmwareNeedUpdate = true;
            updateEtag(gwConfigEtag);

            fwUpdateState = FW_WaitUserConfirm;
            fwUpdateTimer->start(FW_WAIT_USER_TIMEOUT);
            apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateReadyToStart);

            if (fwUpdateStartedByUser)
            {
                startUpdateFirmware();
            }
        }
        return;
    }
    else if (devConnected)
    {
        QString str;
        str.sprintf("0x%08x", fwVersion);

        if (gwFirmwareVersion != str)
        {
            gwFirmwareVersion = str;
            gwConfig["fwversion"] = str;
            updateEtag(gwConfigEtag);
        }

        DBG_Printf(DBG_INFO, "GW firmware version: %s\n", qPrintable(gwFirmwareVersion));

        // if the device is detected check that the firmware version is >= min version
        if ((fwVersion & FW_PLATFORM_MASK) == FW_PLATFORM_RPI)
        {
            if (fwVersion < GW_MIN_RPI_FW_VERSION)
            {
                gwFirmwareVersionUpdate.sprintf("0x%08x", GW_MIN_RPI_FW_VERSION);
                gwFirmwareNeedUpdate = true;
                updateEtag(gwConfigEtag);

                DBG_Printf(DBG_INFO, "GW firmware version shall be updated to: 0x%08x\n", GW_MIN_RPI_FW_VERSION);
                fwUpdateState = FW_WaitUserConfirm;
                fwUpdateTimer->start(FW_WAIT_USER_TIMEOUT);
                apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateReadyToStart);
                return;
            }
            else
            {
                DBG_Printf(DBG_INFO, "GW firmware version is up to date: 0x%08x\n", fwVersion);
                fwUpdateState = FW_Idle;
                fwUpdateTimer->start(FW_IDLE_TIMEOUT_LONG);
                return;
            }
        }

        if (!gwFirmwareVersionUpdate.isEmpty())
        {
            gwFirmwareVersionUpdate.clear();
            updateEtag(gwConfigEtag);
        }
    }

    fwUpdateState = FW_Idle;
    fwUpdateTimer->start(FW_IDLE_TIMEOUT);
}

/*! Checks if devices for firmware update are present.
 */
void DeRestPluginPrivate::checkFirmwareDevices()
{
    deCONZ::DeviceEnumerator devEnumerator;

    fwProcessArgs.clear();

    devEnumerator.listSerialPorts();
    const std::vector<deCONZ::DeviceEntry> &availPorts = devEnumerator.getList();

    std::vector<deCONZ::DeviceEntry>::const_iterator i = availPorts.begin();
    std::vector<deCONZ::DeviceEntry>::const_iterator end = availPorts.end();

    int raspBeeCount = 0;
    int usbDongleCount = 0;
    QString ttyPath;

    for (; i != end; ++i)
    {
        if (i->friendlyName.contains(QLatin1String("ConBee")))
        {
            usbDongleCount++;
        }
        else if (i->friendlyName.contains(QLatin1String("RaspBee")))
        {
            raspBeeCount = 1;
            ttyPath = i->path;
        }
    }

    if (usbDongleCount > 1)
    {
        DBG_Printf(DBG_INFO, "GW firmware update too many USB devices connected, abort\n");
    }
    else if (usbDongleCount == 1)
    {
        DBG_Printf(DBG_INFO, "GW firmware update select USB device\n");
        fwProcessArgs << "-d" << "0";
    }
    else if (raspBeeCount > 0 && usbDongleCount == 0 && !ttyPath.isEmpty())
    {
        DBG_Printf(DBG_INFO, "GW firmware update select %s device\n", qPrintable(ttyPath));
        fwProcessArgs << "-d" << "RaspBee";
    }

    if (!fwProcessArgs.isEmpty())
    {
        fwUpdateState = FW_CheckVersion;
        fwUpdateTimer->start(0);
        return;
    }

    fwUpdateState = FW_Idle;
    fwUpdateTimer->start(FW_IDLE_TIMEOUT);
}
<commit_msg>Add further search location for firmware files<commit_after>/*
 * Copyright (c) 2016 dresden elektronik ingenieurtechnik gmbh.
 * All rights reserved.
 *
 * The software in this package is published under the terms of the BSD
 * style license a copy of which has been included with this distribution in
 * the LICENSE.txt file.
 *
 */

#include <QApplication>
#include <QDesktopServices>
#include <QFile>
#include <QDir>
#include <QString>
#include <QProcess>
#include "de_web_plugin.h"
#include "de_web_plugin_private.h"

#define FW_IDLE_TIMEOUT (10 * 1000)
#define FW_WAIT_UPDATE_READY (2) //s
#define FW_IDLE_TIMEOUT_LONG (240 * 1000)
#define FW_WAIT_USER_TIMEOUT (120 * 1000)

/*! Inits the firmware update manager.
 */
void DeRestPluginPrivate::initFirmwareUpdate()
{
    fwProcess = 0;
    fwUpdateState = FW_Idle;

    Q_ASSERT(apsCtrl);
    apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateIdle);

    fwUpdateStartedByUser = false;
    fwUpdateTimer = new QTimer(this);
    fwUpdateTimer->setSingleShot(true);
    connect(fwUpdateTimer, SIGNAL(timeout()),
            this, SLOT(firmwareUpdateTimerFired()));
    fwUpdateTimer->start(1000);
}

/*! Starts the actual firmware update process.
 */
void DeRestPluginPrivate::updateFirmware()
{
    if (gwFirmwareNeedUpdate)
    {
        gwFirmwareNeedUpdate = false;
    }

    Q_ASSERT(apsCtrl);
    if (apsCtrl->getParameter(deCONZ::ParamFirmwareUpdateActive) == deCONZ::FirmwareUpdateIdle ||
        apsCtrl->getParameter(deCONZ::ParamDeviceConnected) == 1)
    {
        DBG_Printf(DBG_INFO, "GW firmware update conditions not met, abort\n");
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT);
        return;
    }

    QString gcfFlasherBin = qApp->applicationDirPath() + "/GCFFlasher";
#ifdef Q_OS_WIN
    gcfFlasherBin.append(".exe");
    QString bin = gcfFlasherBin;
#elif defined(Q_OS_LINUX) && !defined(ARCH_ARM) // on RPi a normal sudo is ok since we don't need password there
    QString bin = "pkexec";
    gcfFlasherBin = "/usr/bin/GCFFlasher";
    fwProcessArgs.prepend(gcfFlasherBin);
#elif defined(Q_OS_OSX)
    // TODO
    // /usr/bin/osascript -e 'do shell script "make install" with administrator privileges'
    QString bin = "sudo";
    fwProcessArgs.prepend(gcfFlasherBin);
#else
    QString bin = "sudo";
    fwProcessArgs.prepend(gcfFlasherBin);
#endif

    if (!fwProcess)
    {
        fwProcess = new QProcess(this);
    }

    fwProcessArgs << "-f" << fwUpdateFile;

    fwUpdateState = FW_UpdateWaitFinished;
    fwUpdateTimer->start(250);

    fwProcess->start(bin, fwProcessArgs);
}

/*! Observes the firmware update process.
 */
void DeRestPluginPrivate::updateFirmwareWaitFinished()
{
    if (fwProcess)
    {
        if (fwProcess->bytesAvailable())
        {
            QByteArray data = fwProcess->readAllStandardOutput();
            DBG_Printf(DBG_INFO, "%s", qPrintable(data));

            if (apsCtrl->getParameter(deCONZ::ParamFirmwareUpdateActive) != deCONZ::FirmwareUpdateRunning)
            {
                if (data.contains("flashing"))
                {
                    apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateRunning);
                }
            }
        }

        if (fwProcess->state() == QProcess::Starting)
        {
            DBG_Printf(DBG_INFO, "GW firmware update starting ..\n");
        }
        else if (fwProcess->state() == QProcess::Running)
        {
            DBG_Printf(DBG_INFO_L2, "GW firmware update running ..\n");
        }
        else if (fwProcess->state() == QProcess::NotRunning)
        {
            if (fwProcess->exitStatus() == QProcess::NormalExit)
            {
                DBG_Printf(DBG_INFO, "GW firmware update exit code %d\n", fwProcess->exitCode());
            }
            else if (fwProcess->exitStatus() == QProcess::CrashExit)
            {
                DBG_Printf(DBG_INFO, "GW firmware update crashed %s\n", qPrintable(fwProcess->errorString()));
            }

            fwProcess->deleteLater();
            fwProcess = 0;
        }
    }

    // done
    if (fwProcess == 0)
    {
        fwUpdateStartedByUser = false;
        apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateIdle);
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT);
    }
    else // recheck
    {
        fwUpdateTimer->start(250);
    }
}

/*! Starts the device disconnect so that the serial port is released.
 */
void DeRestPluginPrivate::updateFirmwareDisconnectDevice()
{
    Q_ASSERT(apsCtrl);
//    if (apsCtrl->getParameter(deCONZ::ParamFirmwareUpdateActive) == deCONZ::FirmwareUpdateIdle)
//    {
//        if (apsCtrl->getParameter(deCONZ::ParamDeviceConnected) == 1)
//        {
//            DBG_Printf(DBG_INFO, "GW firmware disconnect device before update\n");
//        }
//    }

    if (apsCtrl->getParameter(deCONZ::ParamDeviceConnected) == 1)
    {
        fwUpdateTimer->start(100); // recheck
    }
    else
    {
        DBG_Printf(DBG_INFO, "GW firmware start update (device not connected)\n");
        fwUpdateState = FW_Update;
        fwUpdateTimer->start(0);
    }
}

/*! Starts the firmware update.
 */
bool DeRestPluginPrivate::startUpdateFirmware()
{
    fwUpdateStartedByUser = true;
    if (fwUpdateState == FW_WaitUserConfirm)
    {
        fwUpdateState = FW_DisconnectDevice;
        fwUpdateTimer->start(100);
        return true;
    }

    return false;
}

/*! Delayed trigger to update the firmware.
 */
void DeRestPluginPrivate::firmwareUpdateTimerFired()
{
    if (fwUpdateState == FW_Idle)
    {
        if (gwFirmwareNeedUpdate)
        {
            gwFirmwareNeedUpdate = false;
            updateEtag(gwConfigEtag);
        }
        fwUpdateState = FW_CheckDevices;
        fwUpdateTimer->start(0);
    }
    else if (fwUpdateState == FW_CheckDevices)
    {
        checkFirmwareDevices();
    }
    else if (fwUpdateState == FW_CheckVersion)
    {
        queryFirmwareVersion();
    }
    else if (fwUpdateState == FW_DisconnectDevice)
    {
        updateFirmwareDisconnectDevice();
    }
    else if (fwUpdateState == FW_Update)
    {
        updateFirmware();
    }
    else if (fwUpdateState == FW_UpdateWaitFinished)
    {
        updateFirmwareWaitFinished();
    }
    else if (fwUpdateState == FW_WaitUserConfirm)
    {
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT);
    }
    else
    {
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT);
    }
}

/*! Lazy query of firmware version.
    Because the device might not be connected at first optaining the
    firmware version must be delayed.

    If the firmware is older then the min required firmware for the platform
    and a proper firmware update file exists, the API will announce that a
    firmware update is available.
 */
void DeRestPluginPrivate::queryFirmwareVersion()
{
    Q_ASSERT(apsCtrl);
    if (!apsCtrl)
    {
        return;
    }

    { // check for GCFFlasher binary
        QString gcfFlasherBin = qApp->applicationDirPath() + "/GCFFlasher";
#ifdef Q_OS_WIN
        gcfFlasherBin.append(".exe");
#elif defined(Q_OS_LINUX)
        gcfFlasherBin = "/usr/bin/GCFFlasher";
#endif

        if (!QFile::exists(gcfFlasherBin))
        {
            DBG_Printf(DBG_INFO, "GW update firmware failed, %s doesn't exist\n", qPrintable(gcfFlasherBin));
            fwUpdateState = FW_Idle;
            fwUpdateTimer->start(FW_IDLE_TIMEOUT_LONG);
            return;
        }
    }

    // does the update file exist?
    if (fwUpdateFile.isEmpty())
    {
        QString fileName;
        fileName.sprintf("deCONZ_Rpi_0x%08x.bin.GCF", GW_MIN_RPI_FW_VERSION);

        // search in different locations
        std::vector<QString> paths;
#ifdef Q_OS_LINUX
        paths.push_back(QLatin1String("/usr/share/deCONZ/firmware/"));
#endif
        paths.push_back(deCONZ::getStorageLocation(deCONZ::ApplicationsDataLocation) + QLatin1String("/firmware/"));
        paths.push_back(deCONZ::getStorageLocation(deCONZ::HomeLocation) + QLatin1String("/raspbee_firmware/"));
#ifdef Q_OS_OSX
        QDir dir(qApp->applicationDirPath());
        dir.cdUp();
        dir.cd("Resources");
        paths.push_back(dir.path() + "/");
#endif

        std::vector<QString>::const_iterator i = paths.begin();
        std::vector<QString>::const_iterator end = paths.end();
        for (; i != end; ++i)
        {
            if (QFile::exists(*i + fileName))
            {
                fwUpdateFile = *i + fileName;
                DBG_Printf(DBG_INFO, "GW update firmware found: %s\n", qPrintable(fwUpdateFile));
                break;
            }
        }
    }

    if (fwUpdateFile.isEmpty())
    {
        DBG_Printf(DBG_ERROR, "GW update firmware not found: %s\n", qPrintable(fwUpdateFile));
        fwUpdateState = FW_Idle;
        fwUpdateTimer->start(FW_IDLE_TIMEOUT);
        return;
    }

    uint8_t devConnected = apsCtrl->getParameter(deCONZ::ParamDeviceConnected);
    uint32_t fwVersion = apsCtrl->getParameter(deCONZ::ParamFirmwareVersion);

    Q_ASSERT(!gwFirmwareNeedUpdate);

    if (devConnected == 0 || fwVersion == 0)
    {
        // if even after some time no firmware was detected
        // ASSUME that a device is present and reachable but might not have firmware installed
//        if (getUptime() >= FW_WAIT_UPDATE_READY)
        {
            QString str;
            str.sprintf("0x%08x", GW_MIN_RPI_FW_VERSION);

            gwFirmwareVersion = "0x00000000"; // unknown
            gwFirmwareVersionUpdate = str;
            gwConfig["fwversion"] = gwFirmwareVersion;
            gwFirmwareNeedUpdate = true;
            updateEtag(gwConfigEtag);

            fwUpdateState = FW_WaitUserConfirm;
            fwUpdateTimer->start(FW_WAIT_USER_TIMEOUT);
            apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateReadyToStart);

            if (fwUpdateStartedByUser)
            {
                startUpdateFirmware();
            }
        }
        return;
    }
    else if (devConnected)
    {
        QString str;
        str.sprintf("0x%08x", fwVersion);

        if (gwFirmwareVersion != str)
        {
            gwFirmwareVersion = str;
            gwConfig["fwversion"] = str;
            updateEtag(gwConfigEtag);
        }

        DBG_Printf(DBG_INFO, "GW firmware version: %s\n", qPrintable(gwFirmwareVersion));

        // if the device is detected check that the firmware version is >= min version
        if ((fwVersion & FW_PLATFORM_MASK) == FW_PLATFORM_RPI)
        {
            if (fwVersion < GW_MIN_RPI_FW_VERSION)
            {
                gwFirmwareVersionUpdate.sprintf("0x%08x", GW_MIN_RPI_FW_VERSION);
                gwFirmwareNeedUpdate = true;
                updateEtag(gwConfigEtag);

                DBG_Printf(DBG_INFO, "GW firmware version shall be updated to: 0x%08x\n", GW_MIN_RPI_FW_VERSION);
                fwUpdateState = FW_WaitUserConfirm;
                fwUpdateTimer->start(FW_WAIT_USER_TIMEOUT);
                apsCtrl->setParameter(deCONZ::ParamFirmwareUpdateActive, deCONZ::FirmwareUpdateReadyToStart);
                return;
            }
            else
            {
                DBG_Printf(DBG_INFO, "GW firmware version is up to date: 0x%08x\n", fwVersion);
                fwUpdateState = FW_Idle;
                fwUpdateTimer->start(FW_IDLE_TIMEOUT_LONG);
                return;
            }
        }

        if (!gwFirmwareVersionUpdate.isEmpty())
        {
            gwFirmwareVersionUpdate.clear();
            updateEtag(gwConfigEtag);
        }
    }

    fwUpdateState = FW_Idle;
    fwUpdateTimer->start(FW_IDLE_TIMEOUT);
}

/*! Checks if devices for firmware update are present.
 */
void DeRestPluginPrivate::checkFirmwareDevices()
{
    deCONZ::DeviceEnumerator devEnumerator;

    fwProcessArgs.clear();

    devEnumerator.listSerialPorts();
    const std::vector<deCONZ::DeviceEntry> &availPorts = devEnumerator.getList();

    std::vector<deCONZ::DeviceEntry>::const_iterator i = availPorts.begin();
    std::vector<deCONZ::DeviceEntry>::const_iterator end = availPorts.end();

    int raspBeeCount = 0;
    int usbDongleCount = 0;
    QString ttyPath;

    for (; i != end; ++i)
    {
        if (i->friendlyName.contains(QLatin1String("ConBee")))
        {
            usbDongleCount++;
        }
        else if (i->friendlyName.contains(QLatin1String("RaspBee")))
        {
            raspBeeCount = 1;
            ttyPath = i->path;
        }
    }

    if (usbDongleCount > 1)
    {
        DBG_Printf(DBG_INFO, "GW firmware update too many USB devices connected, abort\n");
    }
    else if (usbDongleCount == 1)
    {
        DBG_Printf(DBG_INFO, "GW firmware update select USB device\n");
        fwProcessArgs << "-d" << "0";
    }
    else if (raspBeeCount > 0 && usbDongleCount == 0 && !ttyPath.isEmpty())
    {
        DBG_Printf(DBG_INFO, "GW firmware update select %s device\n", qPrintable(ttyPath));
        fwProcessArgs << "-d" << "RaspBee";
    }

    if (!fwProcessArgs.isEmpty())
    {
        fwUpdateState = FW_CheckVersion;
        fwUpdateTimer->start(0);
        return;
    }

    fwUpdateState = FW_Idle;
    fwUpdateTimer->start(FW_IDLE_TIMEOUT);
}
<|endoftext|>