<commit_before>/*
 * Copyright (C) 2004-2011  See the AUTHORS file for details.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation.
 */

#include <znc/znc.h>
#include <znc/IRCNetwork.h>
#include <znc/IRCSock.h>

struct reply {
	const char *szReply;
	bool bLastResponse;
};

// TODO this list is far from complete, no errors are handled
static const struct {
	const char *szRequest;
	struct reply vReplies[10];
} vRouteReplies[] = {
	{"WHO", {
		{"352", false},
		{"354", false}, // e.g. Quaknet uses this for WHO #chan %n
		{"403", true}, // No such chan
		{"315", true},
		{NULL, true}
	}},
	{"LIST", {
		{"321", false},
		{"322", false},
		{"323", true},
		{NULL, true}
	}},
	{"NAMES", {
		{"353", false},
		{"366", true},
		// No such nick/channel
		{"401", true},
		{NULL, true},
	}},
	{"LUSERS", {
		{"251", false},
		{"252", false},
		{"253", false},
		{"254", false},
		{"255", false},
		{"265", false},
		{"266", true},
		// We don't handle 250 here since some IRCds don't sent it
		//{"250", true},
		{NULL, true}
	}},
	{"WHOIS", {
		{"311", false},
		{"319", false},
		{"312", false},
		// "<ip> :actually using host"
		{"338", false},
		{"318", true},
		// No such nick/channel
		{"401", true},
		// No such server
		{"402", true},
		{NULL, true}
	}},
	{"PING", {
		{"PONG", true},
		{NULL, true}
	}},
	{"USERHOST", {
		{"302", true},
		{NULL, true}
	}},
	{"TIME", {
		{"391", true},
		{NULL, true}
	}},
	{"WHOWAS", {
		{"312", false},
		{"314", false},
		{"369", true},
		{NULL, true}
	}},
	{"ISON", {
		{"303", true},
		{NULL, true}
	}},
	{"LINKS", {
		{"364", false},
		{"365", true},
		{NULL, true}
	}},
	{"MAP", {
		{"006", false},
		// inspircd
		{"270", false},
		// SilverLeo wants this two added
		{"015", false},
		{"017", true},
		{"007", true},
		{NULL, true}
	}},
	{"TRACE", {
		{"200", false},
		{"205", false},
		{"262", true},
		{NULL, true}
	}},
	{"USERS", {
		{"265", false},
		{"266", true},
		{NULL, true},
	}},
	// This is just a list of all possible /mode replies stuffed together.
	// Since there should never be more than one of these going on, this
	// should work fine and makes the code simpler.
	{"MODE", {
		// MODE I
		{"346", false},
		{"347", true},
		// MODE b
		{"367", false},
		{"368", true},
		// MODE e
		{"348", false},
		{"349", true},
		{NULL, true},
	 }},
	// END (last item!)
	{NULL, {{NULL, true}}}
};

class CRouteTimeout : public CTimer {
public:
	CRouteTimeout(CModule* pModule, unsigned int uInterval, unsigned int uCycles,
			const CString& sLabel, const CString& sDescription)
		: CTimer(pModule, uInterval, uCycles, sLabel, sDescription) {}
	virtual ~CRouteTimeout() {}

protected:
	virtual void RunJob();
};

struct queued_req {
	CString sLine;
	const struct reply *reply;
};

typedef std::map<CClient *, std::vector<struct queued_req> > requestQueue;

class CRouteRepliesMod : public CModule
{
public:
	MODCONSTRUCTOR(CRouteRepliesMod)
	{
		m_pDoing = NULL;
		m_pReplies = NULL;
	}

	virtual ~CRouteRepliesMod() {
		requestQueue::iterator it;

		while (!m_vsPending.empty()) {
			it = m_vsPending.begin();

			while (!it->second.empty()) {
				PutIRC(it->second[0].sLine);
				it->second.erase(it->second.begin());
			}

			m_vsPending.erase(it);
		}
	}

	virtual void OnIRCConnected()
	{
		m_pDoing = NULL;
		m_pReplies = NULL;
		m_vsPending.clear();

		// No way we get a reply, so stop the timer (If it's running)
		RemTimer("RouteTimeout");
	}

	virtual void OnIRCDisconnected()
	{
		OnIRCConnected(); // Let's keep it in one place
	}

	virtual void OnClientDisconnect()
	{
		requestQueue::iterator it;

		if (m_pClient == m_pDoing) {
			// The replies which aren't received yet will be
			// broadcasted to everyone, but at least nothing breaks
			RemTimer("RouteTimeout");
			m_pDoing = NULL;
			m_pReplies = NULL;
		}

		it = m_vsPending.find(m_pClient);

		if (it != m_vsPending.end())
			m_vsPending.erase(it);

		SendRequest();
	}

	virtual EModRet OnRaw(CString& sLine)
	{
		CString sCmd = sLine.Token(1).AsUpper();
		size_t i = 0;

		if (!m_pReplies)
			return CONTINUE;

		// Is this a "not enough arguments" error?
		if (sCmd == "461") {
			// :server 461 nick WHO :Not enough parameters
			CString sOrigCmd = sLine.Token(3);

			if (m_sLastRequest.Token(0).Equals(sOrigCmd)) {
				// This is the reply to the last request
				if (RouteReply(sLine, true))
					return HALTCORE;
				return CONTINUE;
			}
		}

		while (m_pReplies[i].szReply != NULL) {
			if (m_pReplies[i].szReply == sCmd) {
				if (RouteReply(sLine, m_pReplies[i].bLastResponse, sCmd == "353"))
					return HALTCORE;
				return CONTINUE;
			}
			i++;
		}

		// TODO HALTCORE is wrong, it should not be passed to
		// the clients, but the core itself should still handle it!

		return CONTINUE;
	}

	virtual EModRet OnUserRaw(CString& sLine)
	{
		CString sCmd = sLine.Token(0).AsUpper();

		if (!m_pNetwork->GetIRCSock())
			return CONTINUE;

		if (sCmd.Equals("MODE")) {
			// Check if this is a mode request that needs to be handled

			// If there are arguments to a mode change,
			// we must not route it.
			if (!sLine.Token(3, true).empty())
				return CONTINUE;

			// Grab the mode change parameter
			CString sMode = sLine.Token(2);

			// If this is a channel mode request, znc core replies to it
			if (sMode.empty())
				return CONTINUE;

			// Check if this is a mode change or a specific
			// mode request (the later needs to be routed).
			sMode.TrimPrefix("+");
			if (sMode.length() != 1)
				return CONTINUE;

			// Now just check if it's one of the supported modes
			switch (sMode[0]) {
			case 'I':
			case 'b':
			case 'e':
				break;
			default:
				return CONTINUE;
			}

			// Ok, this looks like we should route it.
			// Fall through to the next loop
		}

		for (size_t i = 0; vRouteReplies[i].szRequest != NULL; i++) {
			if (vRouteReplies[i].szRequest == sCmd) {
				struct queued_req req = {
					sLine, vRouteReplies[i].vReplies
				};
				m_vsPending[m_pClient].push_back(req);
				SendRequest();

				return HALTCORE;
			}
		}

		return CONTINUE;
	}

	void Timeout()
	{
		// The timer will be deleted after this by the event loop

		if (GetNV("silent_timeouts") != "yes") {
			PutModule("This module hit a timeout which is possibly a bug.");
			PutModule("To disable this message, do \"/msg " + GetModNick()
					+ " silent yes\"");
			PutModule("Last request: " + m_sLastRequest);
			PutModule("Expected replies: ");

			for (size_t i = 0; m_pReplies[i].szReply != NULL; i++) {
				if (m_pReplies[i].bLastResponse)
					PutModule(m_pReplies[i].szReply +
							CString(" (last)"));
				else
					PutModule(m_pReplies[i].szReply);
			}
		}

		m_pDoing = NULL;
		m_pReplies = NULL;
		SendRequest();
	}

private:
	bool RouteReply(const CString& sLine, bool bFinished = false, bool bIsRaw353 = false)
	{
		if (!m_pDoing)
			return false;

		// 353 needs special treatment due to NAMESX and UHNAMES
		if (bIsRaw353)
			m_pNetwork->GetIRCSock()->ForwardRaw353(sLine, m_pDoing);
		else
			m_pDoing->PutClient(sLine);

		if (bFinished) {
			// Stop the timeout
			RemTimer("RouteTimeout");

			m_pDoing = NULL;
			m_pReplies = NULL;
			SendRequest();
		}

		return true;
	}

	void SendRequest()
	{
		requestQueue::iterator it;

		if (m_pDoing || m_pReplies)
			return;

		if (m_vsPending.empty())
			return;

		it = m_vsPending.begin();

		if (it->second.empty()) {
			m_vsPending.erase(it);
			SendRequest();
			return;
		}

		// When we are called from the timer, we need to remove it.
		// We can't delete it (segfault on return), thus we
		// just stop it. The main loop will delete it.
		CTimer *pTimer = FindTimer("RouteTimeout");
		if (pTimer) {
			pTimer->Stop();
			UnlinkTimer(pTimer);
		}
		AddTimer(new CRouteTimeout(this, 60, 1, "RouteTimeout",
				"Recover from missing / wrong server replies"));

		m_pDoing = it->first;
		m_pReplies = it->second[0].reply;
		m_sLastRequest = it->second[0].sLine;
		PutIRC(it->second[0].sLine);
		it->second.erase(it->second.begin());
	}

	virtual void OnModCommand(const CString& sCommand) {
		const CString sCmd = sCommand.Token(0);
		const CString sArgs = sCommand.Token(1, true);

		if (sCmd.Equals("silent")) {
			if (sArgs.Equals("yes")) {
				SetNV("silent_timeouts", "yes");
				PutModule("Disabled timeout messages");
			} else if (sArgs.Equals("no")) {
				DelNV("silent_timeouts");
				PutModule("Enabled timeout messages");
			} else if (sArgs.empty()) {
				if (GetNV("silent_timeouts") == "yes")
					PutModule("Timeout messages are disabled");
				else
					PutModule("Timeout message are enabled");
			} else
				PutModule("Invalid argument");
		} else {
			PutModule("Available commands: silent [yes/no], silent");
		}
	}

	CClient            *m_pDoing;
	const struct reply *m_pReplies;
	requestQueue        m_vsPending;
	// This field is only used for display purpose.
	CString             m_sLastRequest;
};

void CRouteTimeout::RunJob()
{
	CRouteRepliesMod *pMod = (CRouteRepliesMod *) m_pModule;
	pMod->Timeout();
}

template<> void TModInfo<CRouteRepliesMod>(CModInfo& Info) {
	Info.SetWikiPage("route_replies");
}

NETWORKMODULEDEFS(CRouteRepliesMod, "Send replies (e.g. to /who) to the right client only")
<commit_msg>route_replies: Handle raw 482<commit_after>/*
 * Copyright (C) 2004-2011  See the AUTHORS file for details.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation.
 */

#include <znc/znc.h>
#include <znc/IRCNetwork.h>
#include <znc/IRCSock.h>

struct reply {
	const char *szReply;
	bool bLastResponse;
};

// TODO this list is far from complete, no errors are handled
static const struct {
	const char *szRequest;
	struct reply vReplies[10];
} vRouteReplies[] = {
	{"WHO", {
		{"352", false},
		{"354", false}, // e.g. Quaknet uses this for WHO #chan %n
		{"403", true}, // No such chan
		{"315", true},
		{NULL, true}
	}},
	{"LIST", {
		{"321", false},
		{"322", false},
		{"323", true},
		{NULL, true}
	}},
	{"NAMES", {
		{"353", false},
		{"366", true},
		// No such nick/channel
		{"401", true},
		{NULL, true},
	}},
	{"LUSERS", {
		{"251", false},
		{"252", false},
		{"253", false},
		{"254", false},
		{"255", false},
		{"265", false},
		{"266", true},
		// We don't handle 250 here since some IRCds don't sent it
		//{"250", true},
		{NULL, true}
	}},
	{"WHOIS", {
		{"311", false},
		{"319", false},
		{"312", false},
		// "<ip> :actually using host"
		{"338", false},
		{"318", true},
		// No such nick/channel
		{"401", true},
		// No such server
		{"402", true},
		{NULL, true}
	}},
	{"PING", {
		{"PONG", true},
		{NULL, true}
	}},
	{"USERHOST", {
		{"302", true},
		{NULL, true}
	}},
	{"TIME", {
		{"391", true},
		{NULL, true}
	}},
	{"WHOWAS", {
		{"312", false},
		{"314", false},
		{"369", true},
		{NULL, true}
	}},
	{"ISON", {
		{"303", true},
		{NULL, true}
	}},
	{"LINKS", {
		{"364", false},
		{"365", true},
		{NULL, true}
	}},
	{"MAP", {
		{"006", false},
		// inspircd
		{"270", false},
		// SilverLeo wants this two added
		{"015", false},
		{"017", true},
		{"007", true},
		{NULL, true}
	}},
	{"TRACE", {
		{"200", false},
		{"205", false},
		{"262", true},
		{NULL, true}
	}},
	{"USERS", {
		{"265", false},
		{"266", true},
		{NULL, true},
	}},
	// This is just a list of all possible /mode replies stuffed together.
	// Since there should never be more than one of these going on, this
	// should work fine and makes the code simpler.
	{"MODE", {
		// "You're not a channel operator"
		{"482", true},
		// MODE I
		{"346", false},
		{"347", true},
		// MODE b
		{"367", false},
		{"368", true},
		// MODE e
		{"348", false},
		{"349", true},
		{NULL, true},
	 }},
	// END (last item!)
	{NULL, {{NULL, true}}}
};

class CRouteTimeout : public CTimer {
public:
	CRouteTimeout(CModule* pModule, unsigned int uInterval, unsigned int uCycles,
			const CString& sLabel, const CString& sDescription)
		: CTimer(pModule, uInterval, uCycles, sLabel, sDescription) {}
	virtual ~CRouteTimeout() {}

protected:
	virtual void RunJob();
};

struct queued_req {
	CString sLine;
	const struct reply *reply;
};

typedef std::map<CClient *, std::vector<struct queued_req> > requestQueue;

class CRouteRepliesMod : public CModule
{
public:
	MODCONSTRUCTOR(CRouteRepliesMod)
	{
		m_pDoing = NULL;
		m_pReplies = NULL;
	}

	virtual ~CRouteRepliesMod() {
		requestQueue::iterator it;

		while (!m_vsPending.empty()) {
			it = m_vsPending.begin();

			while (!it->second.empty()) {
				PutIRC(it->second[0].sLine);
				it->second.erase(it->second.begin());
			}

			m_vsPending.erase(it);
		}
	}

	virtual void OnIRCConnected()
	{
		m_pDoing = NULL;
		m_pReplies = NULL;
		m_vsPending.clear();

		// No way we get a reply, so stop the timer (If it's running)
		RemTimer("RouteTimeout");
	}

	virtual void OnIRCDisconnected()
	{
		OnIRCConnected(); // Let's keep it in one place
	}

	virtual void OnClientDisconnect()
	{
		requestQueue::iterator it;

		if (m_pClient == m_pDoing) {
			// The replies which aren't received yet will be
			// broadcasted to everyone, but at least nothing breaks
			RemTimer("RouteTimeout");
			m_pDoing = NULL;
			m_pReplies = NULL;
		}

		it = m_vsPending.find(m_pClient);

		if (it != m_vsPending.end())
			m_vsPending.erase(it);

		SendRequest();
	}

	virtual EModRet OnRaw(CString& sLine)
	{
		CString sCmd = sLine.Token(1).AsUpper();
		size_t i = 0;

		if (!m_pReplies)
			return CONTINUE;

		// Is this a "not enough arguments" error?
		if (sCmd == "461") {
			// :server 461 nick WHO :Not enough parameters
			CString sOrigCmd = sLine.Token(3);

			if (m_sLastRequest.Token(0).Equals(sOrigCmd)) {
				// This is the reply to the last request
				if (RouteReply(sLine, true))
					return HALTCORE;
				return CONTINUE;
			}
		}

		while (m_pReplies[i].szReply != NULL) {
			if (m_pReplies[i].szReply == sCmd) {
				if (RouteReply(sLine, m_pReplies[i].bLastResponse, sCmd == "353"))
					return HALTCORE;
				return CONTINUE;
			}
			i++;
		}

		// TODO HALTCORE is wrong, it should not be passed to
		// the clients, but the core itself should still handle it!

		return CONTINUE;
	}

	virtual EModRet OnUserRaw(CString& sLine)
	{
		CString sCmd = sLine.Token(0).AsUpper();

		if (!m_pNetwork->GetIRCSock())
			return CONTINUE;

		if (sCmd.Equals("MODE")) {
			// Check if this is a mode request that needs to be handled

			// If there are arguments to a mode change,
			// we must not route it.
			if (!sLine.Token(3, true).empty())
				return CONTINUE;

			// Grab the mode change parameter
			CString sMode = sLine.Token(2);

			// If this is a channel mode request, znc core replies to it
			if (sMode.empty())
				return CONTINUE;

			// Check if this is a mode change or a specific
			// mode request (the later needs to be routed).
			sMode.TrimPrefix("+");
			if (sMode.length() != 1)
				return CONTINUE;

			// Now just check if it's one of the supported modes
			switch (sMode[0]) {
			case 'I':
			case 'b':
			case 'e':
				break;
			default:
				return CONTINUE;
			}

			// Ok, this looks like we should route it.
			// Fall through to the next loop
		}

		for (size_t i = 0; vRouteReplies[i].szRequest != NULL; i++) {
			if (vRouteReplies[i].szRequest == sCmd) {
				struct queued_req req = {
					sLine, vRouteReplies[i].vReplies
				};
				m_vsPending[m_pClient].push_back(req);
				SendRequest();

				return HALTCORE;
			}
		}

		return CONTINUE;
	}

	void Timeout()
	{
		// The timer will be deleted after this by the event loop

		if (GetNV("silent_timeouts") != "yes") {
			PutModule("This module hit a timeout which is possibly a bug.");
			PutModule("To disable this message, do \"/msg " + GetModNick()
					+ " silent yes\"");
			PutModule("Last request: " + m_sLastRequest);
			PutModule("Expected replies: ");

			for (size_t i = 0; m_pReplies[i].szReply != NULL; i++) {
				if (m_pReplies[i].bLastResponse)
					PutModule(m_pReplies[i].szReply +
							CString(" (last)"));
				else
					PutModule(m_pReplies[i].szReply);
			}
		}

		m_pDoing = NULL;
		m_pReplies = NULL;
		SendRequest();
	}

private:
	bool RouteReply(const CString& sLine, bool bFinished = false, bool bIsRaw353 = false)
	{
		if (!m_pDoing)
			return false;

		// 353 needs special treatment due to NAMESX and UHNAMES
		if (bIsRaw353)
			m_pNetwork->GetIRCSock()->ForwardRaw353(sLine, m_pDoing);
		else
			m_pDoing->PutClient(sLine);

		if (bFinished) {
			// Stop the timeout
			RemTimer("RouteTimeout");

			m_pDoing = NULL;
			m_pReplies = NULL;
			SendRequest();
		}

		return true;
	}

	void SendRequest()
	{
		requestQueue::iterator it;

		if (m_pDoing || m_pReplies)
			return;

		if (m_vsPending.empty())
			return;

		it = m_vsPending.begin();

		if (it->second.empty()) {
			m_vsPending.erase(it);
			SendRequest();
			return;
		}

		// When we are called from the timer, we need to remove it.
		// We can't delete it (segfault on return), thus we
		// just stop it. The main loop will delete it.
		CTimer *pTimer = FindTimer("RouteTimeout");
		if (pTimer) {
			pTimer->Stop();
			UnlinkTimer(pTimer);
		}
		AddTimer(new CRouteTimeout(this, 60, 1, "RouteTimeout",
				"Recover from missing / wrong server replies"));

		m_pDoing = it->first;
		m_pReplies = it->second[0].reply;
		m_sLastRequest = it->second[0].sLine;
		PutIRC(it->second[0].sLine);
		it->second.erase(it->second.begin());
	}

	virtual void OnModCommand(const CString& sCommand) {
		const CString sCmd = sCommand.Token(0);
		const CString sArgs = sCommand.Token(1, true);

		if (sCmd.Equals("silent")) {
			if (sArgs.Equals("yes")) {
				SetNV("silent_timeouts", "yes");
				PutModule("Disabled timeout messages");
			} else if (sArgs.Equals("no")) {
				DelNV("silent_timeouts");
				PutModule("Enabled timeout messages");
			} else if (sArgs.empty()) {
				if (GetNV("silent_timeouts") == "yes")
					PutModule("Timeout messages are disabled");
				else
					PutModule("Timeout message are enabled");
			} else
				PutModule("Invalid argument");
		} else {
			PutModule("Available commands: silent [yes/no], silent");
		}
	}

	CClient            *m_pDoing;
	const struct reply *m_pReplies;
	requestQueue        m_vsPending;
	// This field is only used for display purpose.
	CString             m_sLastRequest;
};

void CRouteTimeout::RunJob()
{
	CRouteRepliesMod *pMod = (CRouteRepliesMod *) m_pModule;
	pMod->Timeout();
}

template<> void TModInfo<CRouteRepliesMod>(CModInfo& Info) {
	Info.SetWikiPage("route_replies");
}

NETWORKMODULEDEFS(CRouteRepliesMod, "Send replies (e.g. to /who) to the right client only")
<|endoftext|>


<commit_before>#ifndef DUNE_STUFF_FUNCTION_FIXED_HH
#define DUNE_STUFF_FUNCTION_FIXED_HH

#include <dune/common/shared_ptr.hh>
#include <dune/common/exceptions.hh>

#include "interface.hh"
#include <dune/stuff/common/parameter.hh>
#include <dune/stuff/common/color.hh>

namespace Dune {
namespace Stuff {
namespace Function {

template< class DomainFieldImp, int domainDim, class RangeFieldImp, int rangeDim >
class Fixed
  : public Interface< DomainFieldImp, domainDim, RangeFieldImp, rangeDim >
{
public:
  typedef Fixed< DomainFieldImp, domainDim, RangeFieldImp, rangeDim >     ThisType;
  typedef Interface< DomainFieldImp, domainDim, RangeFieldImp, rangeDim > BaseType;

  typedef typename BaseType::DomainFieldType  DomainFieldType;
  static const int                            dimDomain = BaseType::dimDomain;
  typedef typename BaseType::DomainType       DomainType;
  typedef typename BaseType::RangeFieldType   RangeFieldType;
  static const int                            dimRange = BaseType::dimRange;
  typedef typename BaseType::RangeType        RangeType;
  typedef typename BaseType::ParamType        ParamType;

  static std::string id()
  {
    return BaseType::id() + ".fixed";
  }

  Fixed(const Dune::shared_ptr< const BaseType > parametricFunction,
        const ParamType fixedParameter = ParamType())
    : wrappedFunction_(parametricFunction)
    , fixedParam_(fixedParameter)
  {
    DUNE_THROW(Dune::InvalidStateException,
               "\n" << Dune::Stuff::Common::colorStringRed("ERROR:")
               << " given parameter has wrong size (is " << fixedParam_.size()
               << ", should be " << wrappedFunction_->paramSize() << ")!");
  }

  virtual bool parametric() const
  {
    return false;
  }

  virtual std::string name() const
  {
    return "fixed '" + wrappedFunction_->name() + "' for parameter '" + Dune::Stuff::Common::Parameter::report(fixedParam_);
  }

  virtual int order() const
  {
    return wrappedFunction_->order();
  }

  virtual void evaluate(const DomainType& x, RangeType& ret) const
  {
    wrappedFunction_->evaluate(x, fixedParam_, ret);
  }

private:
  const Dune::shared_ptr< const BaseType > wrappedFunction_;
  const ParamType fixedParam_;
}; // class Fixed

} // namespace Function
} // namespace Stuff
} // namespace Dune

#endif // DUNE_STUFF_FUNCTION_FIXED_HH
<commit_msg>[function.fixed] fixed unconditional throw<commit_after>#ifndef DUNE_STUFF_FUNCTION_FIXED_HH
#define DUNE_STUFF_FUNCTION_FIXED_HH

#include <dune/common/shared_ptr.hh>
#include <dune/common/exceptions.hh>

#include "interface.hh"
#include <dune/stuff/common/parameter.hh>
#include <dune/stuff/common/color.hh>

namespace Dune {
namespace Stuff {
namespace Function {

template< class DomainFieldImp, int domainDim, class RangeFieldImp, int rangeDim >
class Fixed
  : public Interface< DomainFieldImp, domainDim, RangeFieldImp, rangeDim >
{
public:
  typedef Fixed< DomainFieldImp, domainDim, RangeFieldImp, rangeDim >     ThisType;
  typedef Interface< DomainFieldImp, domainDim, RangeFieldImp, rangeDim > BaseType;

  typedef typename BaseType::DomainFieldType  DomainFieldType;
  static const int                            dimDomain = BaseType::dimDomain;
  typedef typename BaseType::DomainType       DomainType;
  typedef typename BaseType::RangeFieldType   RangeFieldType;
  static const int                            dimRange = BaseType::dimRange;
  typedef typename BaseType::RangeType        RangeType;
  typedef typename BaseType::ParamType        ParamType;

  static std::string id()
  {
    return BaseType::id() + ".fixed";
  }

  Fixed(const Dune::shared_ptr< const BaseType > parametricFunction,
        const ParamType fixedParameter = ParamType())
    : wrappedFunction_(parametricFunction)
    , fixedParam_(fixedParameter)
  {
    if (fixedParam_.size() != wrappedFunction_->paramSize())
      DUNE_THROW(Dune::InvalidStateException,
                 "\n" << Dune::Stuff::Common::colorStringRed("ERROR:")
                 << " given parameter has wrong size (is " << fixedParam_.size()
                 << ", should be " << wrappedFunction_->paramSize() << ")!");
  }

  virtual bool parametric() const
  {
    return false;
  }

  virtual std::string name() const
  {
    return "fixed '" + wrappedFunction_->name() + "' for parameter '" + Dune::Stuff::Common::Parameter::report(fixedParam_);
  }

  virtual int order() const
  {
    return wrappedFunction_->order();
  }

  virtual void evaluate(const DomainType& x, RangeType& ret) const
  {
    wrappedFunction_->evaluate(x, fixedParam_, ret);
  }

private:
  const Dune::shared_ptr< const BaseType > wrappedFunction_;
  const ParamType fixedParam_;
}; // class Fixed

} // namespace Function
} // namespace Stuff
} // namespace Dune

#endif // DUNE_STUFF_FUNCTION_FIXED_HH
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: XMLIndexTemplateContext.hxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: rt $ $Date: 2004-07-13 08:33:51 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  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., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _XMLOFF_XMLINDEXTEMPLATECONTEXT_HXX_
#define _XMLOFF_XMLINDEXTEMPLATECONTEXT_HXX_

#ifndef _XMLOFF_XMLICTXT_HXX
#include "xmlictxt.hxx"
#endif

#ifndef _XMLOFF_XMLTOKEN_HXX
#include "xmltoken.hxx"
#endif

#include <vector>

#ifndef _COM_SUN_STAR_UNO_REFERENCE_H_
#include <com/sun/star/uno/Reference.h>
#endif

#ifndef _COM_SUN_STAR_UNO_SEQUENCE_H_
#include <com/sun/star/uno/Sequence.h>
#endif

#ifndef _COM_SUN_STAR_BEANS_PROPERTYVALUES_HPP_
#include <com/sun/star/beans/PropertyValues.hpp>
#endif

namespace com { namespace sun { namespace star {
    namespace xml { namespace sax { class XAttributeList; } }
    namespace beans { class XPropertySet; }
} } }
namespace rtl { class OUString; }
struct SvXMLEnumMapEntry;

// constants for the XMLIndexTemplateContext constructor

// TOC and user defined index:
extern const SvXMLEnumMapEntry aLevelNameTOCMap[];
extern const sal_Char* aLevelStylePropNameTOCMap[];
extern const sal_Bool aAllowedTokenTypesTOC[];
extern const sal_Bool aAllowedTokenTypesUser[];

// alphabetical index:
extern const SvXMLEnumMapEntry aLevelNameAlphaMap[];
extern const sal_Char* aLevelStylePropNameAlphaMap[];
extern const sal_Bool aAllowedTokenTypesAlpha[];

// bibliography:
extern const SvXMLEnumMapEntry aLevelNameBibliographyMap[];
extern const sal_Char* aLevelStylePropNameBibliographyMap[];
extern const sal_Bool aAllowedTokenTypesBibliography[];

// table, illustration and object tables:
extern const SvXMLEnumMapEntry* aLevelNameTableMap; // NULL: no outline-level
extern const sal_Char* aLevelStylePropNameTableMap[];
extern const sal_Bool aAllowedTokenTypesTable[];

/**
 * Import index entry templates
 */
class XMLIndexTemplateContext : public SvXMLImportContext
{
    // pick up PropertyValues to be turned into a sequence.
    ::std::vector< ::com::sun::star::beans::PropertyValues > aValueVector;

    ::rtl::OUString sStyleName;

    const SvXMLEnumMapEntry* pOutlineLevelNameMap;
    enum ::xmloff::token::XMLTokenEnum eOutlineLevelAttrName;
    const sal_Char** pOutlineLevelStylePropMap;
    const sal_Bool* pAllowedTokenTypesMap;

    sal_Int32 nOutlineLevel;
    sal_Bool bStyleNameOK;
    sal_Bool bOutlineLevelOK;
    sal_Bool bTOC;

    // PropertySet of current index
    ::com::sun::star::uno::Reference<
        ::com::sun::star::beans::XPropertySet> & rPropertySet;

public:

    // constants made available to other contexts (template entry
    // contexts, in particular)
    const ::rtl::OUString sTokenEntryNumber;
    const ::rtl::OUString sTokenEntryText;
    const ::rtl::OUString sTokenTabStop;
    const ::rtl::OUString sTokenText;
    const ::rtl::OUString sTokenPageNumber;
    const ::rtl::OUString sTokenChapterInfo;
    const ::rtl::OUString sTokenHyperlinkStart;
    const ::rtl::OUString sTokenHyperlinkEnd;
    const ::rtl::OUString sTokenBibliographyDataField;

    const ::rtl::OUString sCharacterStyleName;
    const ::rtl::OUString sTokenType;
    const ::rtl::OUString sText;
    const ::rtl::OUString sTabStopRightAligned;
    const ::rtl::OUString sTabStopPosition;
    const ::rtl::OUString sTabStopFillCharacter;
    const ::rtl::OUString sBibliographyDataField;
    const ::rtl::OUString sChapterFormat;

    const ::rtl::OUString sLevelFormat;
    const ::rtl::OUString sParaStyleLevel;

    TYPEINFO();

    XMLIndexTemplateContext(
        SvXMLImport& rImport,
        ::com::sun::star::uno::Reference<
            ::com::sun::star::beans::XPropertySet> & rPropSet,
        sal_uInt16 nPrfx,
        const ::rtl::OUString& rLocalName,
        const SvXMLEnumMapEntry* aLevelNameMap,
        enum ::xmloff::token::XMLTokenEnum eLevelAttrName,
        const sal_Char** aLevelStylePropNameMap,
        const sal_Bool* aAllowedTokenTypes,
        sal_Bool bTOC=sal_False);

    ~XMLIndexTemplateContext();

    /** add template; to be called by child template entry contexts */
    void addTemplateEntry(
        const ::com::sun::star::beans::PropertyValues& aValues);

protected:

    virtual void StartElement(
        const ::com::sun::star::uno::Reference<
            ::com::sun::star::xml::sax::XAttributeList> & xAttrList);

    virtual void EndElement();

    virtual SvXMLImportContext *CreateChildContext(
        sal_uInt16 nPrefix,
        const ::rtl::OUString& rLocalName,
        const ::com::sun::star::uno::Reference<
            ::com::sun::star::xml::sax::XAttributeList> & xAttrList );
};

#endif
<commit_msg>INTEGRATION: CWS ooo19126 (1.5.298); FILE MERGED 2005/09/05 14:39:55 rt 1.5.298.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: XMLIndexTemplateContext.hxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 15:12:23 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _XMLOFF_XMLINDEXTEMPLATECONTEXT_HXX_
#define _XMLOFF_XMLINDEXTEMPLATECONTEXT_HXX_

#ifndef _XMLOFF_XMLICTXT_HXX
#include "xmlictxt.hxx"
#endif

#ifndef _XMLOFF_XMLTOKEN_HXX
#include "xmltoken.hxx"
#endif

#include <vector>

#ifndef _COM_SUN_STAR_UNO_REFERENCE_H_
#include <com/sun/star/uno/Reference.h>
#endif

#ifndef _COM_SUN_STAR_UNO_SEQUENCE_H_
#include <com/sun/star/uno/Sequence.h>
#endif

#ifndef _COM_SUN_STAR_BEANS_PROPERTYVALUES_HPP_
#include <com/sun/star/beans/PropertyValues.hpp>
#endif

namespace com { namespace sun { namespace star {
    namespace xml { namespace sax { class XAttributeList; } }
    namespace beans { class XPropertySet; }
} } }
namespace rtl { class OUString; }
struct SvXMLEnumMapEntry;

// constants for the XMLIndexTemplateContext constructor

// TOC and user defined index:
extern const SvXMLEnumMapEntry aLevelNameTOCMap[];
extern const sal_Char* aLevelStylePropNameTOCMap[];
extern const sal_Bool aAllowedTokenTypesTOC[];
extern const sal_Bool aAllowedTokenTypesUser[];

// alphabetical index:
extern const SvXMLEnumMapEntry aLevelNameAlphaMap[];
extern const sal_Char* aLevelStylePropNameAlphaMap[];
extern const sal_Bool aAllowedTokenTypesAlpha[];

// bibliography:
extern const SvXMLEnumMapEntry aLevelNameBibliographyMap[];
extern const sal_Char* aLevelStylePropNameBibliographyMap[];
extern const sal_Bool aAllowedTokenTypesBibliography[];

// table, illustration and object tables:
extern const SvXMLEnumMapEntry* aLevelNameTableMap; // NULL: no outline-level
extern const sal_Char* aLevelStylePropNameTableMap[];
extern const sal_Bool aAllowedTokenTypesTable[];

/**
 * Import index entry templates
 */
class XMLIndexTemplateContext : public SvXMLImportContext
{
    // pick up PropertyValues to be turned into a sequence.
    ::std::vector< ::com::sun::star::beans::PropertyValues > aValueVector;

    ::rtl::OUString sStyleName;

    const SvXMLEnumMapEntry* pOutlineLevelNameMap;
    enum ::xmloff::token::XMLTokenEnum eOutlineLevelAttrName;
    const sal_Char** pOutlineLevelStylePropMap;
    const sal_Bool* pAllowedTokenTypesMap;

    sal_Int32 nOutlineLevel;
    sal_Bool bStyleNameOK;
    sal_Bool bOutlineLevelOK;
    sal_Bool bTOC;

    // PropertySet of current index
    ::com::sun::star::uno::Reference<
        ::com::sun::star::beans::XPropertySet> & rPropertySet;

public:

    // constants made available to other contexts (template entry
    // contexts, in particular)
    const ::rtl::OUString sTokenEntryNumber;
    const ::rtl::OUString sTokenEntryText;
    const ::rtl::OUString sTokenTabStop;
    const ::rtl::OUString sTokenText;
    const ::rtl::OUString sTokenPageNumber;
    const ::rtl::OUString sTokenChapterInfo;
    const ::rtl::OUString sTokenHyperlinkStart;
    const ::rtl::OUString sTokenHyperlinkEnd;
    const ::rtl::OUString sTokenBibliographyDataField;

    const ::rtl::OUString sCharacterStyleName;
    const ::rtl::OUString sTokenType;
    const ::rtl::OUString sText;
    const ::rtl::OUString sTabStopRightAligned;
    const ::rtl::OUString sTabStopPosition;
    const ::rtl::OUString sTabStopFillCharacter;
    const ::rtl::OUString sBibliographyDataField;
    const ::rtl::OUString sChapterFormat;

    const ::rtl::OUString sLevelFormat;
    const ::rtl::OUString sParaStyleLevel;

    TYPEINFO();

    XMLIndexTemplateContext(
        SvXMLImport& rImport,
        ::com::sun::star::uno::Reference<
            ::com::sun::star::beans::XPropertySet> & rPropSet,
        sal_uInt16 nPrfx,
        const ::rtl::OUString& rLocalName,
        const SvXMLEnumMapEntry* aLevelNameMap,
        enum ::xmloff::token::XMLTokenEnum eLevelAttrName,
        const sal_Char** aLevelStylePropNameMap,
        const sal_Bool* aAllowedTokenTypes,
        sal_Bool bTOC=sal_False);

    ~XMLIndexTemplateContext();

    /** add template; to be called by child template entry contexts */
    void addTemplateEntry(
        const ::com::sun::star::beans::PropertyValues& aValues);

protected:

    virtual void StartElement(
        const ::com::sun::star::uno::Reference<
            ::com::sun::star::xml::sax::XAttributeList> & xAttrList);

    virtual void EndElement();

    virtual SvXMLImportContext *CreateChildContext(
        sal_uInt16 nPrefix,
        const ::rtl::OUString& rLocalName,
        const ::com::sun::star::uno::Reference<
            ::com::sun::star::xml::sax::XAttributeList> & xAttrList );
};

#endif
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "otbRPCSolver.h"
#include "otbMacro.h"

#include "vnl/algo/vnl_svd.h"
#include "vnl/algo/vnl_matrix_inverse.h"

#include "otbGDALRPCTransformer.h"

namespace otb
{

using PrecisionType = double;
using VectorType = vnl_vector<PrecisionType>;
using MatrixType = vnl_matrix<PrecisionType>;
using DiagonalMatrixType = vnl_diag_matrix<PrecisionType>;

const PrecisionType epsilon = 1e-7;

void UpdateMatrix(const VectorType & f,
                    const std::vector<PrecisionType> & x,
                    const std::vector<PrecisionType> & y,
                    const std::vector<PrecisionType> & z,
                    MatrixType & M)
{
   for(unsigned int i = 0; i < f.size(); i++)
   {
      M[i][0]  = 1;
      M[i][1]  = x[i];
      M[i][2]  = y[i];
      M[i][3]  = z[i];
      M[i][4]  = x[i]*y[i];
      M[i][5]  = x[i]*z[i];
      M[i][6]  = y[i]*z[i];
      M[i][7]  = x[i]*x[i];
      M[i][8]  = y[i]*y[i];
      M[i][9]  = z[i]*z[i];
      M[i][10] = x[i]*y[i]*z[i];
      M[i][11] = x[i]*x[i]*x[i];
      M[i][12] = x[i]*y[i]*y[i];
      M[i][13] = x[i]*z[i]*z[i];
      M[i][14] = x[i]*x[i]*y[i];
      M[i][15] = y[i]*y[i]*y[i];
      M[i][16] = y[i]*z[i]*z[i];
      M[i][17] = x[i]*x[i]*z[i];
      M[i][18] = y[i]*y[i]*z[i];
      M[i][19] = z[i]*z[i]*z[i];
      M[i][20] = -f[i]*x[i];
      M[i][21] = -f[i]*y[i];
      M[i][22] = -f[i]*z[i];
      M[i][23] = -f[i]*x[i]*y[i];
      M[i][24] = -f[i]*x[i]*z[i];
      M[i][25] = -f[i]*y[i]*z[i];
      M[i][26] = -f[i]*x[i]*x[i];
      M[i][27] = -f[i]*y[i]*y[i];
      M[i][28] = -f[i]*z[i]*z[i];
      M[i][29] = -f[i]*x[i]*y[i]*z[i];
      M[i][30] = -f[i]*x[i]*x[i]*x[i];
      M[i][31] = -f[i]*x[i]*y[i]*y[i];
      M[i][32] = -f[i]*x[i]*z[i]*z[i];
      M[i][33] = -f[i]*x[i]*x[i]*y[i];
      M[i][34] = -f[i]*y[i]*y[i]*y[i];
      M[i][35] = -f[i]*y[i]*z[i]*z[i];
      M[i][36] = -f[i]*x[i]*x[i]*z[i];
      M[i][37] = -f[i]*y[i]*y[i]*z[i];
      M[i][38] = -f[i]*z[i]*z[i]*z[i];
   }
}

void UpdateWeights(const std::vector<PrecisionType> & x,
                    const std::vector<PrecisionType> & y,
                    const std::vector<PrecisionType> & z,
                    const VectorType & coeff,
                    DiagonalMatrixType & W)
{
   std::vector<PrecisionType> row(coeff.size());

    for(unsigned int i = 0; i < x.size(); i++)
    {
      W[i] = 0.;

      row[0]  = 1;
      row[1]  = x[i];
      row[2]  = y[i];
      row[3]  = z[i];
      row[4]  = x[i]*y[i];
      row[5]  = x[i]*z[i];
      row[6]  = y[i]*z[i];
      row[7]  = x[i]*x[i];
      row[8]  = y[i]*y[i];
      row[9]  = z[i]*z[i];
      row[10] = x[i]*y[i]*z[i];
      row[11] = x[i]*x[i]*x[i];
      row[12] = x[i]*y[i]*y[i];
      row[13] = x[i]*z[i]*z[i];
      row[14] = x[i]*x[i]*y[i];
      row[15] = y[i]*y[i]*y[i];
      row[16] = y[i]*z[i]*z[i];
      row[17] = x[i]*x[i]*z[i];
      row[18] = y[i]*y[i]*z[i];
      row[19] = z[i]*z[i]*z[i];

      for (unsigned int j =0; j < row.size(); j++)
      {
        W[i] += row[j]*coeff[j];
      }

      if(W[i] > epsilon)
      {
         W[i] = 1.0/W[i];
      }
    }
}

void computeCoefficients(const std::vector<PrecisionType> & f,
                          const std::vector<PrecisionType> & x,
                          const std::vector<PrecisionType> & y,
                          const std::vector<PrecisionType> & z,
                          std::vector<PrecisionType> & outCoeffs)
{
  auto numberOfPoints = f.size();

  // Iteratively reweighted least square

  MatrixType M(numberOfPoints, 39);
  VectorType r(numberOfPoints, numberOfPoints, f.data());
  DiagonalMatrixType weights(numberOfPoints, 1.);

  PrecisionType res = std::numeric_limits<PrecisionType>::max();
  VectorType coeffs;

  for (int i =0; i<10; i++)
  {
    if (res < epsilon)
    {
      break;
    }
    auto w2 = weights * weights;

    UpdateMatrix(r, x, y ,z ,M);

    vnl_svd<PrecisionType>    svd(M.transpose()*w2*M);

    auto diag = svd.W();
    for (unsigned int idx = 0; idx < diag.size(); idx++)
    {
      if(diag[idx] > epsilon)
      {
         diag[idx] = 1.0/diag[idx];
      }
      else
      {
         diag[idx] = 0.0;
      }
    }

    // "svd.V() * diag * svd.U().transpose()" is the inverse of M.transpose()*w2*M
    coeffs =  svd.V() * diag * svd.U().transpose() * M.transpose()*w2*r;

    auto denominator = VectorType(&(coeffs[19]), 20);
    denominator[0] = 1;

    UpdateWeights( x, y ,z , denominator, weights);

    // compute the residual
    auto residual = M.transpose()*w2*(M*coeffs-r);

    auto residualValue = inner_product(residual,residual);
  }

  outCoeffs.assign(coeffs.begin(), coeffs.end());
}

void RPCSolver::Solve(const GCPsContainerType& gcpContainer, PrecisionType& rmsError, Projection::RPCParam& outputParams)
{
  // By default, use elevation provided with ground control points
  bool useElevation = true;
  auto numberOfPoints = gcpContainer.size();

  std::vector<PrecisionType> colNorm;
  colNorm.reserve(numberOfPoints);

  std::vector<PrecisionType> lineNorm;
  lineNorm.reserve(numberOfPoints);

  std::vector<PrecisionType> lonNorm;
  lonNorm.reserve(numberOfPoints);

  std::vector<PrecisionType> latNorm;
  latNorm.reserve(numberOfPoints);

  std::vector<PrecisionType> altNorm;
  altNorm.reserve(numberOfPoints);

  // Check for enough points
  if (numberOfPoints < 20)
  {
    itkGenericExceptionMacro(<< "At least 20 points are required to estimate the 40 parameters of a RPC model without elevation support, and 40 are required "
                                "to estimate the 80 parameters of a RPC model with elevation support. Only "
                             << numberOfPoints << " points were given.");
  }

  // If not enough points are given for a proper estimation of RPC
  // with elevation support, disable elevation. This will result in
  // all coefficients related to elevation set to zero.
  if (numberOfPoints < 40)
  {
    otbGenericWarningMacro("Only " << numberOfPoints << " ground control points are provided, can not estimate a RPC model with elevation support (at "
                                                             "least 40 points required). Elevation support will be disabled for RPC estimation. All "
                                                             "coefficients related to elevation will be set to zero, and elevation will have no effect on the "
                                                             "resulting transform.");
    useElevation = false;
  }

  // Compute Offsets

  // Find the ground points center of mass
  PrecisionType accLat = 0.;
  PrecisionType accLon = 0.;
  PrecisionType accAlt = 0.;

  for (const auto & gcp : gcpContainer)
  {
    const auto & groundPoint = gcp.second;

    accLon += groundPoint[0];
    accLat += groundPoint[1];
    if (useElevation)
    {
      accAlt += groundPoint[2];
    }
  }

  Point3DType groundCenter;
  groundCenter[0] = accLon / numberOfPoints;
  groundCenter[1] = accLat /numberOfPoints;
  groundCenter[2] = useElevation ? accAlt / numberOfPoints : 0.;

  PrecisionType minc = std::numeric_limits<PrecisionType>::max();
  PrecisionType minl = std::numeric_limits<PrecisionType>::max();
  PrecisionType maxc = std::numeric_limits<PrecisionType>::min();
  PrecisionType maxl = std::numeric_limits<PrecisionType>::min();

  for (const auto & gcp : gcpContainer)
  {
    const auto & imagePoint = gcp.first;

    minc = std::min(imagePoint[0], minl);
    maxc = std::max(imagePoint[0], maxc);

    minl = std::min(imagePoint[1], minl);
    maxl = std::max(imagePoint[1], maxl);
  }

  Point2DType imageCenter;
  imageCenter[0] = (minc + maxc -1)/2.0;
  imageCenter[1] = (minl + maxl -1)/2.0;

  auto height = std::abs(minl - maxl) +1;
  auto width = std::abs(minc - maxc) +1;

  PrecisionType maxDeltaLon = std::numeric_limits<PrecisionType>::min();
  PrecisionType maxDeltaLat = std::numeric_limits<PrecisionType>::min();
  PrecisionType maxDeltaAlt = std::numeric_limits<PrecisionType>::min();

  for (const auto & gcp : gcpContainer)
  {
    const auto & imagePoint = gcp.first;
    const auto & groundPoint = gcp.second;
    auto deltaLon = groundPoint[0] - groundCenter[0];
    auto deltaLat = groundPoint[1] - groundCenter[1];
    auto deltaAlt = useElevation ? groundPoint[2] - groundCenter[2] : 0.;
    auto alt = useElevation ? groundPoint[2] : 0.;

    maxDeltaLon = std::max(maxDeltaLon, std::abs(deltaLon));
    maxDeltaLat = std::max(maxDeltaLat, std::abs(deltaLat));
    maxDeltaAlt = std::max(maxDeltaAlt, std::abs(alt));

    colNorm.push_back( (imagePoint[0]- imageCenter[0] -0.5) / width * 2.);
    lineNorm.push_back( (imagePoint[1]- imageCenter[1] -0.5)/ height * 2.);
    lonNorm.push_back(deltaLon);
    latNorm.push_back(deltaLat);
    altNorm.push_back(deltaAlt);
  }

  if(maxDeltaLat < 1.0)
  {
    maxDeltaLat = 1.0;
  }
  else
  {
    for (auto & lat: latNorm)
    {
      lat /= maxDeltaLat;
    }
  }

  if(maxDeltaLon < 1.0)
  {
    maxDeltaLon = 1.0;
  }
  else
  {
    for (auto & lon: lonNorm)
    {
      lon /= maxDeltaLon;
    }
  }

  if(maxDeltaAlt < 1.0)
  {
    maxDeltaAlt = 1.0;
  }
  else
  {
    for (auto & alt: altNorm)
    {
      alt /= maxDeltaAlt;
    }
  }

  std::vector<PrecisionType> colCoeffs;
  computeCoefficients(colNorm, lonNorm, latNorm, altNorm, colCoeffs);

  std::vector<PrecisionType> lineCoeffs;
  computeCoefficients(lineNorm, lonNorm, latNorm, altNorm, lineCoeffs);

    // Offsets
  outputParams.SampleOffset = imageCenter[0];
  outputParams.LineOffset = imageCenter[1];
  outputParams.LonOffset = groundCenter[0];
  outputParams.LatOffset = groundCenter[1];
  outputParams.HeightOffset = groundCenter[2];

  // Scales
  outputParams.SampleScale = width/2.0;
  outputParams.LineScale = height/2.0;
  outputParams.LatScale = maxDeltaLat;
  outputParams.LonScale = maxDeltaLon;
  outputParams.HeightScale = maxDeltaAlt;

  // Line numerator coefficients
  std::copy(lineCoeffs.begin(), lineCoeffs.begin() +20, outputParams.LineNum);
  // Line denominator coefficients
  outputParams.LineDen[0] = 1.;
  std::copy(lineCoeffs.begin()+20, lineCoeffs.end(), outputParams.LineDen +1);

  // Sample numerator coefficients
  std::copy(colCoeffs.begin(), colCoeffs.begin() +20, outputParams.SampleNum);
  // Sample denominator coefficients
  outputParams.SampleDen[0] = 1.;
  std::copy(colCoeffs.begin()+20, colCoeffs.end(), outputParams.SampleDen +1);

  // Compute rmse between input ground point and transformed image points
  GDALRPCTransformer transformer(outputParams, false);

  PrecisionType rmseAcc = 0.;

  for (const auto & gcp : gcpContainer)
  {
    auto outPoint = transformer.InverseTransform(gcp.second);
    rmseAcc += (gcp.first[0] - outPoint[0]) * (gcp.first[0] - outPoint[0])
                + (gcp.first[1] - outPoint[1]) * (gcp.first[1] - outPoint[1]);
  }

  rmsError = std::sqrt(rmseAcc)/numberOfPoints;
}

}<commit_msg>BUG: fix various bugs in RPCSolver<commit_after>/*
 * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "otbRPCSolver.h"
#include "otbMacro.h"

#include "vnl/algo/vnl_svd.h"
#include "vnl/algo/vnl_matrix_inverse.h"

#include "otbGDALRPCTransformer.h"

namespace otb
{

using PrecisionType = double;
using VectorType = vnl_vector<PrecisionType>;
using MatrixType = vnl_matrix<PrecisionType>;
using DiagonalMatrixType = vnl_diag_matrix<PrecisionType>;

const PrecisionType epsilon = 1e-7;

void UpdateMatrix(const VectorType & f,
                    const std::vector<PrecisionType> & x,
                    const std::vector<PrecisionType> & y,
                    const std::vector<PrecisionType> & z,
                    MatrixType & M)
{
   for(unsigned int i = 0; i < f.size(); i++)
   {
      M[i][0]  = 1;
      M[i][1]  = x[i];
      M[i][2]  = y[i];
      M[i][3]  = z[i];
      M[i][4]  = x[i]*y[i];
      M[i][5]  = x[i]*z[i];
      M[i][6]  = y[i]*z[i];
      M[i][7]  = x[i]*x[i];
      M[i][8]  = y[i]*y[i];
      M[i][9]  = z[i]*z[i];
      M[i][10] = x[i]*y[i]*z[i];
      M[i][11] = x[i]*x[i]*x[i];
      M[i][12] = x[i]*y[i]*y[i];
      M[i][13] = x[i]*z[i]*z[i];
      M[i][14] = x[i]*x[i]*y[i];
      M[i][15] = y[i]*y[i]*y[i];
      M[i][16] = y[i]*z[i]*z[i];
      M[i][17] = x[i]*x[i]*z[i];
      M[i][18] = y[i]*y[i]*z[i];
      M[i][19] = z[i]*z[i]*z[i];
      M[i][20] = -f[i]*x[i];
      M[i][21] = -f[i]*y[i];
      M[i][22] = -f[i]*z[i];
      M[i][23] = -f[i]*x[i]*y[i];
      M[i][24] = -f[i]*x[i]*z[i];
      M[i][25] = -f[i]*y[i]*z[i];
      M[i][26] = -f[i]*x[i]*x[i];
      M[i][27] = -f[i]*y[i]*y[i];
      M[i][28] = -f[i]*z[i]*z[i];
      M[i][29] = -f[i]*x[i]*y[i]*z[i];
      M[i][30] = -f[i]*x[i]*x[i]*x[i];
      M[i][31] = -f[i]*x[i]*y[i]*y[i];
      M[i][32] = -f[i]*x[i]*z[i]*z[i];
      M[i][33] = -f[i]*x[i]*x[i]*y[i];
      M[i][34] = -f[i]*y[i]*y[i]*y[i];
      M[i][35] = -f[i]*y[i]*z[i]*z[i];
      M[i][36] = -f[i]*x[i]*x[i]*z[i];
      M[i][37] = -f[i]*y[i]*y[i]*z[i];
      M[i][38] = -f[i]*z[i]*z[i]*z[i];
   }
}

void UpdateWeights(const std::vector<PrecisionType> & x,
                    const std::vector<PrecisionType> & y,
                    const std::vector<PrecisionType> & z,
                    const VectorType & coeff,
                    DiagonalMatrixType & W)
{
   std::vector<PrecisionType> row(coeff.size());

    for(unsigned int i = 0; i < x.size(); i++)
    {
      W[i] = 0.;

      row[0]  = 1;
      row[1]  = x[i];
      row[2]  = y[i];
      row[3]  = z[i];
      row[4]  = x[i]*y[i];
      row[5]  = x[i]*z[i];
      row[6]  = y[i]*z[i];
      row[7]  = x[i]*x[i];
      row[8]  = y[i]*y[i];
      row[9]  = z[i]*z[i];
      row[10] = x[i]*y[i]*z[i];
      row[11] = x[i]*x[i]*x[i];
      row[12] = x[i]*y[i]*y[i];
      row[13] = x[i]*z[i]*z[i];
      row[14] = x[i]*x[i]*y[i];
      row[15] = y[i]*y[i]*y[i];
      row[16] = y[i]*z[i]*z[i];
      row[17] = x[i]*x[i]*z[i];
      row[18] = y[i]*y[i]*z[i];
      row[19] = z[i]*z[i]*z[i];

      for (unsigned int j =0; j < row.size(); j++)
      {
        W[i] += row[j]*coeff[j];
      }

      if(W[i] > epsilon)
      {
         W[i] = 1.0/W[i];
      }
    }
}

void computeCoefficients(const std::vector<PrecisionType> & f,
                          const std::vector<PrecisionType> & x,
                          const std::vector<PrecisionType> & y,
                          const std::vector<PrecisionType> & z,
                          std::vector<PrecisionType> & outCoeffs)
{
  auto numberOfPoints = f.size();

  // Iteratively reweighted least square

  MatrixType M(numberOfPoints, 39);
  VectorType r(numberOfPoints, numberOfPoints, f.data());
  DiagonalMatrixType weights(numberOfPoints, 1.);

  PrecisionType res = std::numeric_limits<PrecisionType>::max();
  VectorType coeffs;

  for (int i =0; i<10; i++)
  {
    if (res < epsilon)
    {
      break;
    }
    auto w2 = weights * weights;

    UpdateMatrix(r, x, y ,z ,M);

    vnl_svd<PrecisionType>    svd(M.transpose()*w2*M);

    auto diag = svd.W();
    for (unsigned int idx = 0; idx < diag.size(); idx++)
    {
      if(diag[idx] > epsilon)
      {
         diag[idx] = 1.0/diag[idx];
      }
      else
      {
         diag[idx] = 0.0;
      }
    }

    // "svd.V() * diag * svd.U().transpose()" is the inverse of M.transpose()*w2*M
    coeffs =  svd.V() * diag * svd.U().transpose() * M.transpose()*w2*r;

    auto denominator = VectorType(&(coeffs[19]), 20);
    denominator[0] = 1;

    UpdateWeights( x, y ,z , denominator, weights);

    // compute the residual
    auto residual = M.transpose()*w2*(M*coeffs-r);

    res = inner_product(residual,residual);
  }

  outCoeffs.assign(coeffs.begin(), coeffs.end());
}

void RPCSolver::Solve(const GCPsContainerType& gcpContainer, PrecisionType& rmsError, Projection::RPCParam& outputParams)
{
  // By default, use elevation provided with ground control points
  bool useElevation = true;
  auto numberOfPoints = gcpContainer.size();

  std::vector<PrecisionType> colNorm;
  colNorm.reserve(numberOfPoints);

  std::vector<PrecisionType> lineNorm;
  lineNorm.reserve(numberOfPoints);

  std::vector<PrecisionType> lonNorm;
  lonNorm.reserve(numberOfPoints);

  std::vector<PrecisionType> latNorm;
  latNorm.reserve(numberOfPoints);

  std::vector<PrecisionType> altNorm;
  altNorm.reserve(numberOfPoints);

  // Check for enough points
  if (numberOfPoints < 20)
  {
    itkGenericExceptionMacro(<< "At least 20 points are required to estimate the 40 parameters of a RPC model without elevation support, and 40 are required "
                                "to estimate the 80 parameters of a RPC model with elevation support. Only "
                             << numberOfPoints << " points were given.");
  }

  // If not enough points are given for a proper estimation of RPC
  // with elevation support, disable elevation. This will result in
  // all coefficients related to elevation set to zero.
  if (numberOfPoints < 40)
  {
    otbGenericWarningMacro("Only " << numberOfPoints << " ground control points are provided, can not estimate a RPC model with elevation support (at "
                                                             "least 40 points required). Elevation support will be disabled for RPC estimation. All "
                                                             "coefficients related to elevation will be set to zero, and elevation will have no effect on the "
                                                             "resulting transform.");
    useElevation = false;
  }

  // Compute Offsets

  // Find the ground points center of mass
  PrecisionType accLat = 0.;
  PrecisionType accLon = 0.;
  PrecisionType accAlt = 0.;

  for (const auto & gcp : gcpContainer)
  {
    const auto & groundPoint = gcp.second;

    accLon += groundPoint[0];
    accLat += groundPoint[1];
    if (useElevation)
    {
      accAlt += groundPoint[2];
    }
  }

  Point3DType groundCenter;
  groundCenter[0] = accLon / numberOfPoints;
  groundCenter[1] = accLat /numberOfPoints;
  groundCenter[2] = useElevation ? accAlt / numberOfPoints : 0.;

  PrecisionType minc = std::numeric_limits<PrecisionType>::max();
  PrecisionType minl = std::numeric_limits<PrecisionType>::max();
  PrecisionType maxc = std::numeric_limits<PrecisionType>::min();
  PrecisionType maxl = std::numeric_limits<PrecisionType>::min();

  for (const auto & gcp : gcpContainer)
  {
    const auto & imagePoint = gcp.first;

    minc = std::min(imagePoint[0], minc);
    maxc = std::max(imagePoint[0], maxc);

    minl = std::min(imagePoint[1], minl);
    maxl = std::max(imagePoint[1], maxl);
  }

  Point2DType imageCenter;
  imageCenter[0] = (minc + maxc -1)/2.0;
  imageCenter[1] = (minl + maxl -1)/2.0;

  auto height = std::abs(minl - maxl) +1;
  auto width = std::abs(minc - maxc) +1;

  PrecisionType maxDeltaLon = std::numeric_limits<PrecisionType>::min();
  PrecisionType maxDeltaLat = std::numeric_limits<PrecisionType>::min();
  PrecisionType maxDeltaAlt = std::numeric_limits<PrecisionType>::min();

  for (const auto & gcp : gcpContainer)
  {
    const auto & imagePoint = gcp.first;
    const auto & groundPoint = gcp.second;
    auto deltaLon = groundPoint[0] - groundCenter[0];
    auto deltaLat = groundPoint[1] - groundCenter[1];
    auto deltaAlt = useElevation ? groundPoint[2] - groundCenter[2] : 0.;
    auto alt = useElevation ? groundPoint[2] : 0.;

    maxDeltaLon = std::max(maxDeltaLon, std::abs(deltaLon));
    maxDeltaLat = std::max(maxDeltaLat, std::abs(deltaLat));
    maxDeltaAlt = std::max(maxDeltaAlt, std::abs(alt));

    colNorm.push_back( (imagePoint[0]- imageCenter[0] -0.5) / width * 2.);
    lineNorm.push_back( (imagePoint[1]- imageCenter[1] -0.5)/ height * 2.);
    lonNorm.push_back(deltaLon);
    latNorm.push_back(deltaLat);
    altNorm.push_back(deltaAlt);
  }

  if(maxDeltaLat < 1.0)
  {
    maxDeltaLat = 1.0;
  }
  else
  {
    for (auto & lat: latNorm)
    {
      lat /= maxDeltaLat;
    }
  }

  if(maxDeltaLon < 1.0)
  {
    maxDeltaLon = 1.0;
  }
  else
  {
    for (auto & lon: lonNorm)
    {
      lon /= maxDeltaLon;
    }
  }

  if(maxDeltaAlt < 1.0)
  {
    maxDeltaAlt = 1.0;
  }
  else
  {
    for (auto & alt: altNorm)
    {
      alt /= maxDeltaAlt;
    }
  }

  std::vector<PrecisionType> colCoeffs;
  computeCoefficients(colNorm, lonNorm, latNorm, altNorm, colCoeffs);

  std::vector<PrecisionType> lineCoeffs;
  computeCoefficients(lineNorm, lonNorm, latNorm, altNorm, lineCoeffs);

    // Offsets
  outputParams.SampleOffset = imageCenter[0];
  outputParams.LineOffset = imageCenter[1];
  outputParams.LonOffset = groundCenter[0];
  outputParams.LatOffset = groundCenter[1];
  outputParams.HeightOffset = groundCenter[2];

  // Scales
  outputParams.SampleScale = width/2.0;
  outputParams.LineScale = height/2.0;
  outputParams.LatScale = maxDeltaLat;
  outputParams.LonScale = maxDeltaLon;
  outputParams.HeightScale = maxDeltaAlt;

  // Line numerator coefficients
  std::copy(lineCoeffs.begin(), lineCoeffs.begin() +20, outputParams.LineNum);
  // Line denominator coefficients
  outputParams.LineDen[0] = 1.;
  std::copy(lineCoeffs.begin()+20, lineCoeffs.end(), outputParams.LineDen +1);

  // Sample numerator coefficients
  std::copy(colCoeffs.begin(), colCoeffs.begin() +20, outputParams.SampleNum);
  // Sample denominator coefficients
  outputParams.SampleDen[0] = 1.;
  std::copy(colCoeffs.begin()+20, colCoeffs.end(), outputParams.SampleDen +1);

  // Compute rmse between input ground point and transformed image points
  GDALRPCTransformer transformer(outputParams, false);

  PrecisionType rmseAcc = 0.;

  for (const auto & gcp : gcpContainer)
  {
    auto outPoint = transformer.InverseTransform(gcp.second);
    rmseAcc += (gcp.first[0] - outPoint[0]) * (gcp.first[0] - outPoint[0])
                + (gcp.first[1] - outPoint[1]) * (gcp.first[1] - outPoint[1]);
  }

  rmsError = std::sqrt(rmseAcc/numberOfPoints);
}

}<|endoftext|>


<commit_before>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the LICENSE
 * file in the root directory of this source tree.
 */
#include "FlipperConnectionManagerImpl.h"
#include <folly/String.h>
#include <folly/futures/Future.h>
#include <folly/io/async/AsyncSocketException.h>
#include <folly/io/async/SSLContext.h>
#include <folly/json.h>
#include <rsocket/Payload.h>
#include <rsocket/RSocket.h>
#include <rsocket/transports/tcp/TcpConnectionFactory.h>
#include <stdexcept>
#include <thread>
#include "ConnectionContextStore.h"
#include "FireAndForgetBasedFlipperResponder.h"
#include "FlipperRSocketResponder.h"
#include "FlipperResponderImpl.h"
#include "FlipperStep.h"
#include "Log.h"
#include "yarpl/Single.h"

#define WRONG_THREAD_EXIT_MSG \
  "ERROR: Aborting flipper initialization because it's not running in the flipper thread."

static constexpr int reconnectIntervalSeconds = 2;
static constexpr int connectionKeepaliveSeconds = 10;

static constexpr int maxPayloadSize = 0xFFFFFF;

// Not a public-facing version number.
// Used for compatibility checking with desktop flipper.
// To be bumped for every core platform interface change.
static constexpr int sdkVersion = 1;

namespace facebook {
namespace flipper {

class ConnectionEvents : public rsocket::RSocketConnectionEvents {
 private:
  FlipperConnectionManagerImpl* websocket_;

 public:
  ConnectionEvents(FlipperConnectionManagerImpl* websocket)
      : websocket_(websocket) {}

  void onConnected() {
    websocket_->isOpen_ = true;
    if (websocket_->connectionIsTrusted_) {
      websocket_->callbacks_->onConnected();
    }
  }

  void onDisconnected(const folly::exception_wrapper&) {
    if (!websocket_->isOpen_)
      return;
    websocket_->isOpen_ = false;
    if (websocket_->connectionIsTrusted_) {
      websocket_->connectionIsTrusted_ = false;
      websocket_->callbacks_->onDisconnected();
    }
    websocket_->reconnect();
  }

  void onClosed(const folly::exception_wrapper& e) {
    onDisconnected(e);
  }
};

FlipperConnectionManagerImpl::FlipperConnectionManagerImpl(
    FlipperInitConfig config,
    std::shared_ptr<FlipperState> state,
    std::shared_ptr<ConnectionContextStore> contextStore)
    : deviceData_(config.deviceData),
      flipperState_(state),
      insecurePort(config.insecurePort),
      securePort(config.securePort),
      flipperEventBase_(config.callbackWorker),
      connectionEventBase_(config.connectionWorker),
      contextStore_(contextStore) {
  CHECK_THROW(config.callbackWorker, std::invalid_argument);
  CHECK_THROW(config.connectionWorker, std::invalid_argument);
}

FlipperConnectionManagerImpl::~FlipperConnectionManagerImpl() {
  stop();
}

void FlipperConnectionManagerImpl::start() {
  auto step = flipperState_->start("Start connection thread");

  folly::makeFuture()
      .via(flipperEventBase_->getEventBase())
      .delayed(std::chrono::milliseconds(0))
      .thenValue([this, step](auto&&) {
        step->complete();
        startSync();
      });
}

void FlipperConnectionManagerImpl::startSync() {
  if (!isRunningInOwnThread()) {
    log(WRONG_THREAD_EXIT_MSG);
    return;
  }
  if (isOpen()) {
    log("Already connected");
    return;
  }
  bool isClientSetupStep = isCertificateExchangeNeeded();
  auto step = flipperState_->start(
      isClientSetupStep ? "Establish pre-setup connection"
                        : "Establish main connection");
  try {
    if (isClientSetupStep) {
      doCertificateExchange();
    } else {
      connectSecurely();
    }
    step->complete();
  } catch (const folly::AsyncSocketException& e) {
    if (e.getType() == folly::AsyncSocketException::NOT_OPEN ||
        e.getType() == folly::AsyncSocketException::NETWORK_ERROR) {
      // The expected code path when flipper desktop is not running.
      // Don't count as a failed attempt, or it would invalidate the connection
      // files for no reason. On iOS devices, we can always connect to the local
      // port forwarding server even when it can't connect to flipper. In that
      // case we get a Network error instead of a Port not open error, so we
      // treat them the same.
      step->fail(
          "No route to flipper found. Is flipper desktop running? Retrying...");
    } else {
      log(e.what());
      failedConnectionAttempts_++;
      step->fail(e.what());
    }
    reconnect();
  } catch (const std::exception& e) {
    log(e.what());
    step->fail(e.what());
    failedConnectionAttempts_++;
    reconnect();
  }
}

void FlipperConnectionManagerImpl::doCertificateExchange() {
  rsocket::SetupParameters parameters;
  folly::SocketAddress address;

  parameters.payload = rsocket::Payload(folly::toJson(folly::dynamic::object(
      "os", deviceData_.os)("device", deviceData_.device)(
      "app", deviceData_.app)("sdk_version", sdkVersion)));
  address.setFromHostPort(deviceData_.host, insecurePort);

  auto connectingInsecurely = flipperState_->start("Connect insecurely");
  connectionIsTrusted_ = false;
  client_ =
      rsocket::RSocket::createConnectedClient(
          std::make_unique<rsocket::TcpConnectionFactory>(
              *connectionEventBase_->getEventBase(), std::move(address)),
          std::move(parameters),
          nullptr,
          std::chrono::seconds(connectionKeepaliveSeconds), // keepaliveInterval
          nullptr, // stats
          std::make_shared<ConnectionEvents>(this))
          .get();
  connectingInsecurely->complete();

  requestSignedCertFromFlipper();
}

void FlipperConnectionManagerImpl::connectSecurely() {
  rsocket::SetupParameters parameters;
  folly::SocketAddress address;

  auto loadingDeviceId = flipperState_->start("Load Device Id");
  auto deviceId = contextStore_->getDeviceId();
  if (deviceId.compare("unknown")) {
    loadingDeviceId->complete();
  }
  parameters.payload = rsocket::Payload(
      folly::toJson(folly::dynamic::object("os", deviceData_.os)(
          "device", deviceData_.device)("device_id", deviceId)(
          "app", deviceData_.app)("sdk_version", sdkVersion)));
  address.setFromHostPort(deviceData_.host, securePort);

  std::shared_ptr<folly::SSLContext> sslContext =
      contextStore_->getSSLContext();
  auto connectingSecurely = flipperState_->start("Connect securely");
  connectionIsTrusted_ = true;
  client_ =
      rsocket::RSocket::createConnectedClient(
          std::make_unique<rsocket::TcpConnectionFactory>(
              *connectionEventBase_->getEventBase(),
              std::move(address),
              std::move(sslContext)),
          std::move(parameters),
          std::make_shared<FlipperRSocketResponder>(this, connectionEventBase_),
          std::chrono::seconds(connectionKeepaliveSeconds), // keepaliveInterval
          nullptr, // stats
          std::make_shared<ConnectionEvents>(this))
          .get();
  connectingSecurely->complete();
  failedConnectionAttempts_ = 0;
}

void FlipperConnectionManagerImpl::reconnect() {
  folly::makeFuture()
      .via(flipperEventBase_->getEventBase())
      .delayed(std::chrono::seconds(reconnectIntervalSeconds))
      .thenValue([this](auto&&) { startSync(); });
}

void FlipperConnectionManagerImpl::stop() {
  if (client_) {
    client_->disconnect();
  }
  client_ = nullptr;
}

bool FlipperConnectionManagerImpl::isOpen() const {
  return isOpen_ && connectionIsTrusted_;
}

void FlipperConnectionManagerImpl::setCallbacks(Callbacks* callbacks) {
  callbacks_ = callbacks;
}

void FlipperConnectionManagerImpl::sendMessage(const folly::dynamic& message) {
  flipperEventBase_->add([this, message]() {
    try {
      rsocket::Payload payload = toRSocketPayload(message);
      if (client_) {
        client_->getRequester()
            ->fireAndForget(std::move(payload))
            ->subscribe([]() {});
      }
    } catch (std::length_error& e) {
      // Skip sending messages that are too large.
      log(e.what());
      return;
    }
  });
}

void FlipperConnectionManagerImpl::onMessageReceived(
    const folly::dynamic& message,
    std::unique_ptr<FlipperResponder> responder) {
  callbacks_->onMessageReceived(message, std::move(responder));
}

bool FlipperConnectionManagerImpl::isCertificateExchangeNeeded() {
  if (failedConnectionAttempts_ >= 2) {
    return true;
  }

  auto step = flipperState_->start("Check required certificates are present");
  bool hasRequiredFiles = contextStore_->hasRequiredFiles();
  if (hasRequiredFiles) {
    step->complete();
  }
  return !hasRequiredFiles;
}

void FlipperConnectionManagerImpl::requestSignedCertFromFlipper() {
  auto generatingCSR = flipperState_->start("Generate CSR");
  std::string csr = contextStore_->getCertificateSigningRequest();
  generatingCSR->complete();

  folly::dynamic message =
      folly::dynamic::object("method", "signCertificate")("csr", csr.c_str())(
          "destination", contextStore_->getCertificateDirectoryPath().c_str());
  auto gettingCert = flipperState_->start("Getting cert from desktop");

  flipperEventBase_->add([this, message, gettingCert]() {
    client_->getRequester()
        ->requestResponse(rsocket::Payload(folly::toJson(message)))
        ->subscribe(
            [this, gettingCert](rsocket::Payload p) {
              auto response = p.moveDataToString();
              if (!response.empty()) {
                folly::dynamic config = folly::parseJson(response);
                contextStore_->storeConnectionConfig(config);
              }
              gettingCert->complete();
              log("Certificate exchange complete.");
              // Disconnect after message sending is complete.
              // This will trigger a reconnect which should use the secure
              // channel.
              // TODO: Connect immediately, without waiting for reconnect
              client_ = nullptr;
            },
            [this, message](folly::exception_wrapper e) {
              e.handle(
                  [&](rsocket::ErrorWithPayload& errorWithPayload) {
                    std::string errorMessage =
                        errorWithPayload.payload.moveDataToString();

                    if (errorMessage.compare("not implemented")) {
                      log("Desktop failed to provide certificates. Error from flipper desktop:\n" +
                          errorMessage);
                      client_ = nullptr;
                    } else {
                      sendLegacyCertificateRequest(message);
                    }
                  },
                  [e](...) {
                    log(("Error during certificate exchange:" + e.what())
                            .c_str());
                  });
            });
  });
  failedConnectionAttempts_ = 0;
}

void FlipperConnectionManagerImpl::sendLegacyCertificateRequest(
    folly::dynamic message) {
  // Desktop is using an old version of Flipper.
  // Fall back to fireAndForget, instead of requestResponse.
  auto sendingRequest =
      flipperState_->start("Sending fallback certificate request");
  client_->getRequester()
      ->fireAndForget(rsocket::Payload(folly::toJson(message)))
      ->subscribe([this, sendingRequest]() {
        sendingRequest->complete();
        folly::dynamic config = folly::dynamic::object();
        contextStore_->storeConnectionConfig(config);
        client_ = nullptr;
      });
}

bool FlipperConnectionManagerImpl::isRunningInOwnThread() {
  return flipperEventBase_->isInEventBaseThread();
}

rsocket::Payload toRSocketPayload(dynamic data) {
  std::string json = folly::toJson(data);
  rsocket::Payload payload = rsocket::Payload(json);
  auto payloadLength = payload.data->computeChainDataLength();
  if (payloadLength > maxPayloadSize) {
    auto logMessage =
        std::string(
            "Error: Skipping sending message larger than max rsocket payload: ") +
        json.substr(0, 100) + "...";
    log(logMessage);
    DCHECK_LE(payloadLength, maxPayloadSize);
    throw new std::length_error(logMessage);
  }

  return payload;
}

} // namespace flipper
} // namespace facebook
<commit_msg>Add date and time reminder to diagnostic screen<commit_after>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the LICENSE
 * file in the root directory of this source tree.
 */
#include "FlipperConnectionManagerImpl.h"
#include <folly/String.h>
#include <folly/futures/Future.h>
#include <folly/io/async/AsyncSocketException.h>
#include <folly/io/async/SSLContext.h>
#include <folly/json.h>
#include <rsocket/Payload.h>
#include <rsocket/RSocket.h>
#include <rsocket/transports/tcp/TcpConnectionFactory.h>
#include <stdexcept>
#include <thread>
#include "ConnectionContextStore.h"
#include "FireAndForgetBasedFlipperResponder.h"
#include "FlipperRSocketResponder.h"
#include "FlipperResponderImpl.h"
#include "FlipperStep.h"
#include "Log.h"
#include "yarpl/Single.h"

#define WRONG_THREAD_EXIT_MSG \
  "ERROR: Aborting flipper initialization because it's not running in the flipper thread."

static constexpr int reconnectIntervalSeconds = 2;
static constexpr int connectionKeepaliveSeconds = 10;

static constexpr int maxPayloadSize = 0xFFFFFF;

// Not a public-facing version number.
// Used for compatibility checking with desktop flipper.
// To be bumped for every core platform interface change.
static constexpr int sdkVersion = 1;

namespace facebook {
namespace flipper {

class ConnectionEvents : public rsocket::RSocketConnectionEvents {
 private:
  FlipperConnectionManagerImpl* websocket_;

 public:
  ConnectionEvents(FlipperConnectionManagerImpl* websocket)
      : websocket_(websocket) {}

  void onConnected() {
    websocket_->isOpen_ = true;
    if (websocket_->connectionIsTrusted_) {
      websocket_->callbacks_->onConnected();
    }
  }

  void onDisconnected(const folly::exception_wrapper&) {
    if (!websocket_->isOpen_)
      return;
    websocket_->isOpen_ = false;
    if (websocket_->connectionIsTrusted_) {
      websocket_->connectionIsTrusted_ = false;
      websocket_->callbacks_->onDisconnected();
    }
    websocket_->reconnect();
  }

  void onClosed(const folly::exception_wrapper& e) {
    onDisconnected(e);
  }
};

FlipperConnectionManagerImpl::FlipperConnectionManagerImpl(
    FlipperInitConfig config,
    std::shared_ptr<FlipperState> state,
    std::shared_ptr<ConnectionContextStore> contextStore)
    : deviceData_(config.deviceData),
      flipperState_(state),
      insecurePort(config.insecurePort),
      securePort(config.securePort),
      flipperEventBase_(config.callbackWorker),
      connectionEventBase_(config.connectionWorker),
      contextStore_(contextStore) {
  CHECK_THROW(config.callbackWorker, std::invalid_argument);
  CHECK_THROW(config.connectionWorker, std::invalid_argument);
}

FlipperConnectionManagerImpl::~FlipperConnectionManagerImpl() {
  stop();
}

void FlipperConnectionManagerImpl::start() {
  auto step = flipperState_->start("Start connection thread");

  folly::makeFuture()
      .via(flipperEventBase_->getEventBase())
      .delayed(std::chrono::milliseconds(0))
      .thenValue([this, step](auto&&) {
        step->complete();
        startSync();
      });
}

void FlipperConnectionManagerImpl::startSync() {
  if (!isRunningInOwnThread()) {
    log(WRONG_THREAD_EXIT_MSG);
    return;
  }
  if (isOpen()) {
    log("Already connected");
    return;
  }
  bool isClientSetupStep = isCertificateExchangeNeeded();
  auto step = flipperState_->start(
      isClientSetupStep ? "Establish pre-setup connection"
                        : "Establish main connection");
  try {
    if (isClientSetupStep) {
      doCertificateExchange();
    } else {
      connectSecurely();
    }
    step->complete();
  } catch (const folly::AsyncSocketException& e) {
    if (e.getType() == folly::AsyncSocketException::NOT_OPEN ||
        e.getType() == folly::AsyncSocketException::NETWORK_ERROR) {
      // The expected code path when flipper desktop is not running.
      // Don't count as a failed attempt, or it would invalidate the connection
      // files for no reason. On iOS devices, we can always connect to the local
      // port forwarding server even when it can't connect to flipper. In that
      // case we get a Network error instead of a Port not open error, so we
      // treat them the same.
      step->fail(
          "No route to flipper found. Is flipper desktop running? Retrying...");
    } else {
      if (e.getType() == folly::AsyncSocketException::SSL_ERROR) {
        auto message = std::string(e.what()) +
            "\nMake sure the date and time of your device is up to date.";
        log(message);
        step->fail(message);
      } else {
        log(e.what());
        step->fail(e.what());
      }
      failedConnectionAttempts_++;
    }
    reconnect();
  } catch (const std::exception& e) {
    log(e.what());
    step->fail(e.what());
    failedConnectionAttempts_++;
    reconnect();
  }
}

void FlipperConnectionManagerImpl::doCertificateExchange() {
  rsocket::SetupParameters parameters;
  folly::SocketAddress address;

  parameters.payload = rsocket::Payload(folly::toJson(folly::dynamic::object(
      "os", deviceData_.os)("device", deviceData_.device)(
      "app", deviceData_.app)("sdk_version", sdkVersion)));
  address.setFromHostPort(deviceData_.host, insecurePort);

  auto connectingInsecurely = flipperState_->start("Connect insecurely");
  connectionIsTrusted_ = false;
  client_ =
      rsocket::RSocket::createConnectedClient(
          std::make_unique<rsocket::TcpConnectionFactory>(
              *connectionEventBase_->getEventBase(), std::move(address)),
          std::move(parameters),
          nullptr,
          std::chrono::seconds(connectionKeepaliveSeconds), // keepaliveInterval
          nullptr, // stats
          std::make_shared<ConnectionEvents>(this))
          .get();
  connectingInsecurely->complete();

  requestSignedCertFromFlipper();
}

void FlipperConnectionManagerImpl::connectSecurely() {
  rsocket::SetupParameters parameters;
  folly::SocketAddress address;

  auto loadingDeviceId = flipperState_->start("Load Device Id");
  auto deviceId = contextStore_->getDeviceId();
  if (deviceId.compare("unknown")) {
    loadingDeviceId->complete();
  }
  parameters.payload = rsocket::Payload(
      folly::toJson(folly::dynamic::object("os", deviceData_.os)(
          "device", deviceData_.device)("device_id", deviceId)(
          "app", deviceData_.app)("sdk_version", sdkVersion)));
  address.setFromHostPort(deviceData_.host, securePort);

  std::shared_ptr<folly::SSLContext> sslContext =
      contextStore_->getSSLContext();
  auto connectingSecurely = flipperState_->start("Connect securely");
  connectionIsTrusted_ = true;
  client_ =
      rsocket::RSocket::createConnectedClient(
          std::make_unique<rsocket::TcpConnectionFactory>(
              *connectionEventBase_->getEventBase(),
              std::move(address),
              std::move(sslContext)),
          std::move(parameters),
          std::make_shared<FlipperRSocketResponder>(this, connectionEventBase_),
          std::chrono::seconds(connectionKeepaliveSeconds), // keepaliveInterval
          nullptr, // stats
          std::make_shared<ConnectionEvents>(this))
          .get();
  connectingSecurely->complete();
  failedConnectionAttempts_ = 0;
}

void FlipperConnectionManagerImpl::reconnect() {
  folly::makeFuture()
      .via(flipperEventBase_->getEventBase())
      .delayed(std::chrono::seconds(reconnectIntervalSeconds))
      .thenValue([this](auto&&) { startSync(); });
}

void FlipperConnectionManagerImpl::stop() {
  if (client_) {
    client_->disconnect();
  }
  client_ = nullptr;
}

bool FlipperConnectionManagerImpl::isOpen() const {
  return isOpen_ && connectionIsTrusted_;
}

void FlipperConnectionManagerImpl::setCallbacks(Callbacks* callbacks) {
  callbacks_ = callbacks;
}

void FlipperConnectionManagerImpl::sendMessage(const folly::dynamic& message) {
  flipperEventBase_->add([this, message]() {
    try {
      rsocket::Payload payload = toRSocketPayload(message);
      if (client_) {
        client_->getRequester()
            ->fireAndForget(std::move(payload))
            ->subscribe([]() {});
      }
    } catch (std::length_error& e) {
      // Skip sending messages that are too large.
      log(e.what());
      return;
    }
  });
}

void FlipperConnectionManagerImpl::onMessageReceived(
    const folly::dynamic& message,
    std::unique_ptr<FlipperResponder> responder) {
  callbacks_->onMessageReceived(message, std::move(responder));
}

bool FlipperConnectionManagerImpl::isCertificateExchangeNeeded() {
  if (failedConnectionAttempts_ >= 2) {
    return true;
  }

  auto step = flipperState_->start("Check required certificates are present");
  bool hasRequiredFiles = contextStore_->hasRequiredFiles();
  if (hasRequiredFiles) {
    step->complete();
  }
  return !hasRequiredFiles;
}

void FlipperConnectionManagerImpl::requestSignedCertFromFlipper() {
  auto generatingCSR = flipperState_->start("Generate CSR");
  std::string csr = contextStore_->getCertificateSigningRequest();
  generatingCSR->complete();

  folly::dynamic message =
      folly::dynamic::object("method", "signCertificate")("csr", csr.c_str())(
          "destination", contextStore_->getCertificateDirectoryPath().c_str());
  auto gettingCert = flipperState_->start("Getting cert from desktop");

  flipperEventBase_->add([this, message, gettingCert]() {
    client_->getRequester()
        ->requestResponse(rsocket::Payload(folly::toJson(message)))
        ->subscribe(
            [this, gettingCert](rsocket::Payload p) {
              auto response = p.moveDataToString();
              if (!response.empty()) {
                folly::dynamic config = folly::parseJson(response);
                contextStore_->storeConnectionConfig(config);
              }
              gettingCert->complete();
              log("Certificate exchange complete.");
              // Disconnect after message sending is complete.
              // This will trigger a reconnect which should use the secure
              // channel.
              // TODO: Connect immediately, without waiting for reconnect
              client_ = nullptr;
            },
            [this, message](folly::exception_wrapper e) {
              e.handle(
                  [&](rsocket::ErrorWithPayload& errorWithPayload) {
                    std::string errorMessage =
                        errorWithPayload.payload.moveDataToString();

                    if (errorMessage.compare("not implemented")) {
                      log("Desktop failed to provide certificates. Error from flipper desktop:\n" +
                          errorMessage);
                      client_ = nullptr;
                    } else {
                      sendLegacyCertificateRequest(message);
                    }
                  },
                  [e](...) {
                    log(("Error during certificate exchange:" + e.what())
                            .c_str());
                  });
            });
  });
  failedConnectionAttempts_ = 0;
}

void FlipperConnectionManagerImpl::sendLegacyCertificateRequest(
    folly::dynamic message) {
  // Desktop is using an old version of Flipper.
  // Fall back to fireAndForget, instead of requestResponse.
  auto sendingRequest =
      flipperState_->start("Sending fallback certificate request");
  client_->getRequester()
      ->fireAndForget(rsocket::Payload(folly::toJson(message)))
      ->subscribe([this, sendingRequest]() {
        sendingRequest->complete();
        folly::dynamic config = folly::dynamic::object();
        contextStore_->storeConnectionConfig(config);
        client_ = nullptr;
      });
}

bool FlipperConnectionManagerImpl::isRunningInOwnThread() {
  return flipperEventBase_->isInEventBaseThread();
}

rsocket::Payload toRSocketPayload(dynamic data) {
  std::string json = folly::toJson(data);
  rsocket::Payload payload = rsocket::Payload(json);
  auto payloadLength = payload.data->computeChainDataLength();
  if (payloadLength > maxPayloadSize) {
    auto logMessage =
        std::string(
            "Error: Skipping sending message larger than max rsocket payload: ") +
        json.substr(0, 100) + "...";
    log(logMessage);
    DCHECK_LE(payloadLength, maxPayloadSize);
    throw new std::length_error(logMessage);
  }

  return payload;
}

} // namespace flipper
} // namespace facebook
<|endoftext|>