<commit_before>/*
Highly Optimized Object-Oriented Molecular Dynamics (HOOMD) Open
Source Software License
Copyright (c) 2008 Ames Laboratory Iowa State University
All rights reserved.

Redistribution and use of HOOMD, in source and binary forms, with or
without modification, are permitted, provided that the following
conditions are met:

* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names HOOMD's
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.

Disclaimer

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND
CONTRIBUTORS ``AS IS''  AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.

IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS  BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/

// $Id$
// $URL$

#ifdef WIN32
#pragma warning( push )
#pragma warning( disable : 4103 4244 )
#endif

#include <iostream>

//! Name the unit test module
#define BOOST_TEST_MODULE UtilityClassesTests
#include "boost_utf_configure.h"

#include <math.h>
#include "ClockSource.h"
#include "Profiler.h"
#include "Variant.h"

//! Helper macro for testing if two numbers are close
#define MY_BOOST_CHECK_CLOSE(a,b,c) BOOST_CHECK_CLOSE(a,double(b),double(c))
//! Helper macro for testing if a number is small
#define MY_BOOST_CHECK_SMALL(a,c) BOOST_CHECK_SMALL(a,double(c))

//! Tolerance
double tol = 1e-3;

/*! \file utils_test.cc
	\brief Unit tests for ClockSource, Profiler, and Variant
	\ingroup unit_tests
*/

using namespace std;

// the clock test depends on timing and thus should not be run in automatic builds.
// uncomment to test by hand if the test seems to be behaving poorly

//! perform some simple checks on the clock source code
/*BOOST_AUTO_TEST_CASE(ClockSource_test)
	{
	ClockSource c1;
	int64_t t = c1.getTime();
	// c.getTime() should read 0, but we can't expect it to be exact, so allow a tolerance
	BOOST_CHECK(abs(int(t)) <= 1000000);

	// test timing a whole second
	ClockSource c2;
	int64_t t1 = c2.getTime();
	Sleep(1000);
	int64_t t2 = c2.getTime();

	BOOST_CHECK(abs(int(t2 - t1 - int64_t(1000000000))) <= 20000000);*/

	// unfortunately, testing of microsecond timing with a sleep routine is out of the question
	// the following test code tests the ability of the timer to read nearby values
	/*ClockSource c4;
	int64_t times[100];
	for (int i = 0; i < 100; i++)
		{
		times[i] = c4.getTime();
		}
	for (int i = 0; i < 100; i++)
		{
		cout << times[i] << endl;
		}*/

	// test copying timers
	// operator=
/*	c1 = c2;
	t1 = c1.getTime();
	t2 = c2.getTime();
	BOOST_CHECK(abs(int(t1-t2)) <= 1000000);
	// copy constructor
	ClockSource c3(c1);
	t1 = c1.getTime();
	t2 = c3.getTime();
	BOOST_CHECK(abs(int(t1-t2)) <= 1000000);

	// test the ability of the clock source to format values
	BOOST_CHECK_EQUAL(ClockSource::formatHMS(0), string("00:00:00"));
	BOOST_CHECK_EQUAL(ClockSource::formatHMS(int64_t(1000000000)), string("00:00:01"));
	BOOST_CHECK_EQUAL(ClockSource::formatHMS(int64_t(1000000000)*int64_t(11)), string("00:00:11"));
	BOOST_CHECK_EQUAL(ClockSource::formatHMS(int64_t(1000000000)*int64_t(65)), string("00:01:05"));
	BOOST_CHECK_EQUAL(ClockSource::formatHMS(int64_t(1000000000)*int64_t(3678)), string("01:01:18"));
	}*/

//! perform some simple checks on the profiler code
BOOST_AUTO_TEST_CASE(Profiler_test)
	{
	// ProfileDataElem tests
	// constructor test
	ProfileDataElem p;
	BOOST_CHECK(p.getChildElapsedTime() == 0);
	BOOST_CHECK(p.getTotalFlopCount() == 0);
	BOOST_CHECK(p.getTotalMemByteCount() == 0);

	// build up a tree and test its getTotal members
	p.m_elapsed_time = 1;
	p.m_flop_count = 2;
	p.m_mem_byte_count = 3;
	BOOST_CHECK(p.getChildElapsedTime() == 0);
	BOOST_CHECK(p.getTotalFlopCount() == 2);
	BOOST_CHECK(p.getTotalMemByteCount() == 3);

	p.m_children["A"].m_elapsed_time = 4;
	p.m_children["A"].m_flop_count = 5;
	p.m_children["A"].m_mem_byte_count = 6;
	BOOST_CHECK(p.getChildElapsedTime() == 4);
	BOOST_CHECK(p.getTotalFlopCount() == 7);
	BOOST_CHECK(p.getTotalMemByteCount() == 9);

	p.m_children["B"].m_elapsed_time = 7;
	p.m_children["B"].m_flop_count = 8;
	p.m_children["B"].m_mem_byte_count = 9;
	BOOST_CHECK(p.getChildElapsedTime() == 4+7);
	BOOST_CHECK(p.getTotalFlopCount() == 7+8);
	BOOST_CHECK(p.getTotalMemByteCount() == 9+9);

	p.m_children["A"].m_children["C"].m_elapsed_time = 10;
	p.m_children["A"].m_children["C"].m_flop_count = 11;
	p.m_children["A"].m_children["C"].m_mem_byte_count = 12;
	BOOST_CHECK(p.getChildElapsedTime() == 4+7);
	BOOST_CHECK(p.getTotalFlopCount() == 7+8+11);
	BOOST_CHECK(p.getTotalMemByteCount() == 9+9+12);

	Profiler prof("Main");
	prof.push("Loading");
	Sleep(500);
	prof.pop();
	prof.push("Neighbor");
	Sleep(1000);
	prof.pop(int64_t(1e6), int64_t(1e6));

	prof.push("Pair");
	prof.push("Load");
	Sleep(1000);
	prof.pop(int64_t(1e9), int64_t(1e9));
	prof.push("Work");
	Sleep(1000);
	prof.pop(int64_t(10e9), int64_t(100));
	prof.push("Unload");
	Sleep(1000);
	prof.pop(int64_t(100), int64_t(1e9));
	prof.pop();

	std::cout << prof;

	// This code attempts to reproduce the problem found in ticket #50
	Profiler prof2("test");
	prof2.push("test1");
	//Changing this slep value much lower than 100 results in the bug.
	Sleep(000);
	prof2.pop(100, 100);
	std::cout << prof2;

	}

//! perform some simple checks on the variant types
BOOST_AUTO_TEST_CASE(Variant_test)
	{
	Variant v;
	BOOST_CHECK_EQUAL(v.getValue(0), 0.0);
	BOOST_CHECK_EQUAL(v.getValue(100000), 0.0);
	v.setOffset(1000);
	BOOST_CHECK_EQUAL(v.getValue(0), 0.0);
	BOOST_CHECK_EQUAL(v.getValue(100000), 0.0);
	}

//! perform some simple checks on the variant types
BOOST_AUTO_TEST_CASE(VariantConst_test)
	{
	double val = 10.5;
	VariantConst v(val);
	BOOST_CHECK_EQUAL(v.getValue(0), val);
	BOOST_CHECK_EQUAL(v.getValue(100000), val);
	v.setOffset(1000);
	BOOST_CHECK_EQUAL(v.getValue(0), val);
	BOOST_CHECK_EQUAL(v.getValue(100000), val);
	}

//! perform some simple checks on the variant types
BOOST_AUTO_TEST_CASE(VariantLinear_test1)
	{
	double val = 10.5;
	VariantLinear v;
	v.setPoint(500, val);
	BOOST_CHECK_EQUAL(v.getValue(0), val);
	BOOST_CHECK_EQUAL(v.getValue(500), val);
	BOOST_CHECK_EQUAL(v.getValue(100000), val);
	v.setOffset(1000);
	BOOST_CHECK_EQUAL(v.getValue(0), val);
	BOOST_CHECK_EQUAL(v.getValue(500), val);
	BOOST_CHECK_EQUAL(v.getValue(100000), val);
	}

//! perform some simple checks on the variant types
BOOST_AUTO_TEST_CASE(VariantLinear_test2)
	{
	VariantLinear v;
	v.setPoint(500, 10.0);
	v.setPoint(1000, 20.0);

	MY_BOOST_CHECK_CLOSE(v.getValue(0), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(500), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(750), 15.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1000), 20.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1500), 20.0, tol);
	v.setOffset(1000);
	MY_BOOST_CHECK_CLOSE(v.getValue(0), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1000), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1500), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1750), 15.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(2000), 20.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(2500), 20.0, tol);
	}

//! perform some simple checks on the variant types
BOOST_AUTO_TEST_CASE(VariantLinear_test3)
	{
	VariantLinear v;
	v.setPoint(500, 10.0);
	v.setPoint(1000, 20.0);
	v.setPoint(2000, 50.0);

	MY_BOOST_CHECK_CLOSE(v.getValue(0), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(500), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(750), 15.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1000), 20.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1500), 35.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(2000), 50.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(2500), 50.0, tol);
	v.setOffset(1000);
	MY_BOOST_CHECK_CLOSE(v.getValue(0), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1000), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1500), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1750), 15.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(2000), 20.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(2500), 35.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(3000), 50.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(3500), 50.0, tol);

	// mix up the order to make sure it works no matter what
	MY_BOOST_CHECK_CLOSE(v.getValue(3000), 50.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1500), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(0), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(2000), 20.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(2500), 35.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1000), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1750), 15.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(3500), 50.0, tol);
	}

#ifdef WIN32
#pragma warning( pop )
#endif
<commit_msg>Added missing boost include needed for older boost versions.<commit_after>/*
Highly Optimized Object-Oriented Molecular Dynamics (HOOMD) Open
Source Software License
Copyright (c) 2008 Ames Laboratory Iowa State University
All rights reserved.

Redistribution and use of HOOMD, in source and binary forms, with or
without modification, are permitted, provided that the following
conditions are met:

* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names HOOMD's
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.

Disclaimer

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND
CONTRIBUTORS ``AS IS''  AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.

IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS  BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/

// $Id$
// $URL$

#ifdef WIN32
#pragma warning( push )
#pragma warning( disable : 4103 4244 )
#endif

#include <iostream>

//! Name the unit test module
#define BOOST_TEST_MODULE UtilityClassesTests
#include "boost_utf_configure.h"

#include <math.h>
#include "ClockSource.h"
#include "Profiler.h"
#include "Variant.h"

#include <boost/test/floating_point_comparison.hpp>

//! Helper macro for testing if two numbers are close
#define MY_BOOST_CHECK_CLOSE(a,b,c) BOOST_CHECK_CLOSE(a,double(b),double(c))
//! Helper macro for testing if a number is small
#define MY_BOOST_CHECK_SMALL(a,c) BOOST_CHECK_SMALL(a,double(c))

//! Tolerance
double tol = 1e-3;

/*! \file utils_test.cc
	\brief Unit tests for ClockSource, Profiler, and Variant
	\ingroup unit_tests
*/

using namespace std;

// the clock test depends on timing and thus should not be run in automatic builds.
// uncomment to test by hand if the test seems to be behaving poorly

//! perform some simple checks on the clock source code
/*BOOST_AUTO_TEST_CASE(ClockSource_test)
	{
	ClockSource c1;
	int64_t t = c1.getTime();
	// c.getTime() should read 0, but we can't expect it to be exact, so allow a tolerance
	BOOST_CHECK(abs(int(t)) <= 1000000);

	// test timing a whole second
	ClockSource c2;
	int64_t t1 = c2.getTime();
	Sleep(1000);
	int64_t t2 = c2.getTime();

	BOOST_CHECK(abs(int(t2 - t1 - int64_t(1000000000))) <= 20000000);*/

	// unfortunately, testing of microsecond timing with a sleep routine is out of the question
	// the following test code tests the ability of the timer to read nearby values
	/*ClockSource c4;
	int64_t times[100];
	for (int i = 0; i < 100; i++)
		{
		times[i] = c4.getTime();
		}
	for (int i = 0; i < 100; i++)
		{
		cout << times[i] << endl;
		}*/

	// test copying timers
	// operator=
/*	c1 = c2;
	t1 = c1.getTime();
	t2 = c2.getTime();
	BOOST_CHECK(abs(int(t1-t2)) <= 1000000);
	// copy constructor
	ClockSource c3(c1);
	t1 = c1.getTime();
	t2 = c3.getTime();
	BOOST_CHECK(abs(int(t1-t2)) <= 1000000);

	// test the ability of the clock source to format values
	BOOST_CHECK_EQUAL(ClockSource::formatHMS(0), string("00:00:00"));
	BOOST_CHECK_EQUAL(ClockSource::formatHMS(int64_t(1000000000)), string("00:00:01"));
	BOOST_CHECK_EQUAL(ClockSource::formatHMS(int64_t(1000000000)*int64_t(11)), string("00:00:11"));
	BOOST_CHECK_EQUAL(ClockSource::formatHMS(int64_t(1000000000)*int64_t(65)), string("00:01:05"));
	BOOST_CHECK_EQUAL(ClockSource::formatHMS(int64_t(1000000000)*int64_t(3678)), string("01:01:18"));
	}*/

//! perform some simple checks on the profiler code
BOOST_AUTO_TEST_CASE(Profiler_test)
	{
	// ProfileDataElem tests
	// constructor test
	ProfileDataElem p;
	BOOST_CHECK(p.getChildElapsedTime() == 0);
	BOOST_CHECK(p.getTotalFlopCount() == 0);
	BOOST_CHECK(p.getTotalMemByteCount() == 0);

	// build up a tree and test its getTotal members
	p.m_elapsed_time = 1;
	p.m_flop_count = 2;
	p.m_mem_byte_count = 3;
	BOOST_CHECK(p.getChildElapsedTime() == 0);
	BOOST_CHECK(p.getTotalFlopCount() == 2);
	BOOST_CHECK(p.getTotalMemByteCount() == 3);

	p.m_children["A"].m_elapsed_time = 4;
	p.m_children["A"].m_flop_count = 5;
	p.m_children["A"].m_mem_byte_count = 6;
	BOOST_CHECK(p.getChildElapsedTime() == 4);
	BOOST_CHECK(p.getTotalFlopCount() == 7);
	BOOST_CHECK(p.getTotalMemByteCount() == 9);

	p.m_children["B"].m_elapsed_time = 7;
	p.m_children["B"].m_flop_count = 8;
	p.m_children["B"].m_mem_byte_count = 9;
	BOOST_CHECK(p.getChildElapsedTime() == 4+7);
	BOOST_CHECK(p.getTotalFlopCount() == 7+8);
	BOOST_CHECK(p.getTotalMemByteCount() == 9+9);

	p.m_children["A"].m_children["C"].m_elapsed_time = 10;
	p.m_children["A"].m_children["C"].m_flop_count = 11;
	p.m_children["A"].m_children["C"].m_mem_byte_count = 12;
	BOOST_CHECK(p.getChildElapsedTime() == 4+7);
	BOOST_CHECK(p.getTotalFlopCount() == 7+8+11);
	BOOST_CHECK(p.getTotalMemByteCount() == 9+9+12);

	Profiler prof("Main");
	prof.push("Loading");
	Sleep(500);
	prof.pop();
	prof.push("Neighbor");
	Sleep(1000);
	prof.pop(int64_t(1e6), int64_t(1e6));

	prof.push("Pair");
	prof.push("Load");
	Sleep(1000);
	prof.pop(int64_t(1e9), int64_t(1e9));
	prof.push("Work");
	Sleep(1000);
	prof.pop(int64_t(10e9), int64_t(100));
	prof.push("Unload");
	Sleep(1000);
	prof.pop(int64_t(100), int64_t(1e9));
	prof.pop();

	std::cout << prof;

	// This code attempts to reproduce the problem found in ticket #50
	Profiler prof2("test");
	prof2.push("test1");
	//Changing this slep value much lower than 100 results in the bug.
	Sleep(000);
	prof2.pop(100, 100);
	std::cout << prof2;

	}

//! perform some simple checks on the variant types
BOOST_AUTO_TEST_CASE(Variant_test)
	{
	Variant v;
	BOOST_CHECK_EQUAL(v.getValue(0), 0.0);
	BOOST_CHECK_EQUAL(v.getValue(100000), 0.0);
	v.setOffset(1000);
	BOOST_CHECK_EQUAL(v.getValue(0), 0.0);
	BOOST_CHECK_EQUAL(v.getValue(100000), 0.0);
	}

//! perform some simple checks on the variant types
BOOST_AUTO_TEST_CASE(VariantConst_test)
	{
	double val = 10.5;
	VariantConst v(val);
	BOOST_CHECK_EQUAL(v.getValue(0), val);
	BOOST_CHECK_EQUAL(v.getValue(100000), val);
	v.setOffset(1000);
	BOOST_CHECK_EQUAL(v.getValue(0), val);
	BOOST_CHECK_EQUAL(v.getValue(100000), val);
	}

//! perform some simple checks on the variant types
BOOST_AUTO_TEST_CASE(VariantLinear_test1)
	{
	double val = 10.5;
	VariantLinear v;
	v.setPoint(500, val);
	BOOST_CHECK_EQUAL(v.getValue(0), val);
	BOOST_CHECK_EQUAL(v.getValue(500), val);
	BOOST_CHECK_EQUAL(v.getValue(100000), val);
	v.setOffset(1000);
	BOOST_CHECK_EQUAL(v.getValue(0), val);
	BOOST_CHECK_EQUAL(v.getValue(500), val);
	BOOST_CHECK_EQUAL(v.getValue(100000), val);
	}

//! perform some simple checks on the variant types
BOOST_AUTO_TEST_CASE(VariantLinear_test2)
	{
	VariantLinear v;
	v.setPoint(500, 10.0);
	v.setPoint(1000, 20.0);

	MY_BOOST_CHECK_CLOSE(v.getValue(0), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(500), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(750), 15.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1000), 20.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1500), 20.0, tol);
	v.setOffset(1000);
	MY_BOOST_CHECK_CLOSE(v.getValue(0), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1000), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1500), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1750), 15.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(2000), 20.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(2500), 20.0, tol);
	}

//! perform some simple checks on the variant types
BOOST_AUTO_TEST_CASE(VariantLinear_test3)
	{
	VariantLinear v;
	v.setPoint(500, 10.0);
	v.setPoint(1000, 20.0);
	v.setPoint(2000, 50.0);

	MY_BOOST_CHECK_CLOSE(v.getValue(0), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(500), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(750), 15.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1000), 20.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1500), 35.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(2000), 50.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(2500), 50.0, tol);
	v.setOffset(1000);
	MY_BOOST_CHECK_CLOSE(v.getValue(0), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1000), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1500), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1750), 15.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(2000), 20.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(2500), 35.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(3000), 50.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(3500), 50.0, tol);

	// mix up the order to make sure it works no matter what
	MY_BOOST_CHECK_CLOSE(v.getValue(3000), 50.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1500), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(0), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(2000), 20.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(2500), 35.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1000), 10.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(1750), 15.0, tol);
	MY_BOOST_CHECK_CLOSE(v.getValue(3500), 50.0, tol);
	}

#ifdef WIN32
#pragma warning( pop )
#endif
<|endoftext|>


<commit_before>//
//  Multigrid.cpp
//  Vortex2D
//

#include "Multigrid.h"

#include <Vortex2D/Engine/Pressure.h>

namespace Vortex2D { namespace Fluid {

Depth::Depth(const glm::ivec2& size)
{
    auto s = size;
    mDepths.push_back(s);

    const float min_size = 16.0f;
    while (s.x > min_size && s.y > min_size)
    {
        if (s.x % 2 != 0 || s.y % 2 != 0) throw std::runtime_error("Invalid multigrid size");

        s = s / glm::ivec2(2);
        mDepths.push_back(s);
    }
}

int Depth::GetMaxDepth() const
{
    return mDepths.size() - 1;
}

glm::ivec2 Depth::GetDepthSize(int i) const
{
    assert(i < mDepths.size());
    return mDepths[i];
}

Multigrid::Multigrid(const Renderer::Device& device, const glm::ivec2& size, float delta, bool statistics)
    : mDepth(size)
    , mDelta(delta)
    , mResidualWork(device, size, "../Vortex2D/Residual.comp.spv")
    , mTransfer(device)
    , mPhiScaleWork(device, size, "../Vortex2D/PhiScale.comp.spv")
    , mDampedJacobi(device, size, "../Vortex2D/DampedJacobi.comp.spv")
    , mBuildHierarchies(device, false)
    , mEnableStatistics(statistics)
    , mStatistics(device)
{
    for (int i = 1; i <= mDepth.GetMaxDepth(); i++)
    {
        auto s = mDepth.GetDepthSize(i);
        mDatas.emplace_back(device, s);

        mSolidPhis.emplace_back(device, s);
        mLiquidPhis.emplace_back(device, s);

        mLiquidPhis.back().ExtrapolateInit(mSolidPhis.back());
    }

    for (int i = 0; i < mDepth.GetMaxDepth(); i++)
    {
        auto s = mDepth.GetDepthSize(i);
        mResiduals.emplace_back(device, s.x*s.y);
    }

    for (int i = 0; i <= mDepth.GetMaxDepth(); i++)
    {
        auto s = mDepth.GetDepthSize(i);
        mXs.emplace_back(device, s.x*s.y);
    }

    mSmoothers.resize(2 * (mDepth.GetMaxDepth() + 1));
    mResidualWorkBound.resize(mDepth.GetMaxDepth() + 1);
}

void Multigrid::Init(Renderer::GenericBuffer& d,
                     Renderer::GenericBuffer& l,
                     Renderer::GenericBuffer& b,
                     Renderer::GenericBuffer& pressure)

{
    mPressure = &pressure;

    mResidualWorkBound[0] =
                mResidualWork.Bind({pressure, d, l, b, mResiduals[0]});
    mSmoothers[0] = mDampedJacobi.Bind({pressure, mXs[0], d, l, b});
    mSmoothers[1] = mDampedJacobi.Bind({mXs[0], pressure, d, l, b});

    auto s = mDepth.GetDepthSize(0);
    mTransfer.InitRestrict(0, s, mResiduals[0], d, mDatas[0].B, mDatas[0].Diagonal);
    mTransfer.InitProlongate(0, s, pressure, d, mDatas[0].X, mDatas[0].Diagonal);
}

void Multigrid::BuildHierarchiesInit(Pressure& pressure,
                                     Renderer::Texture& solidPhi,
                                     Renderer::Texture& liquidPhi)
{
    auto s = mDepth.GetDepthSize(1);
    mLiquidPhiScaleWorkBound.push_back(mPhiScaleWork.Bind(s, {liquidPhi, mLiquidPhis[0]}));
    mSolidPhiScaleWorkBound.push_back(mPhiScaleWork.Bind(s, {solidPhi, mSolidPhis[0]}));

    BindRecursive(pressure, 1);

    mBuildHierarchies.Record([&](vk::CommandBuffer commandBuffer)
    {
        for (std::size_t i = 0; i < mDepth.GetMaxDepth(); i++)
        {
            mLiquidPhiScaleWorkBound[i].Record(commandBuffer);
            mLiquidPhis[i].Barrier(commandBuffer,
                                   vk::ImageLayout::eGeneral,
                                   vk::AccessFlagBits::eShaderWrite,
                                   vk::ImageLayout::eGeneral,
                                   vk::AccessFlagBits::eShaderRead);

            mSolidPhiScaleWorkBound[i].Record(commandBuffer);
            mSolidPhis[i].Barrier(commandBuffer,
                                  vk::ImageLayout::eGeneral,
                                  vk::AccessFlagBits::eShaderWrite,
                                  vk::ImageLayout::eGeneral,
                                  vk::AccessFlagBits::eShaderRead);

            mLiquidPhis[i].ExtrapolateRecord(commandBuffer);

            mMatrixBuildBound[i].PushConstant(commandBuffer, 8, mDelta);
            mMatrixBuildBound[i].Record(commandBuffer);
            mDatas[i].Diagonal.Barrier(commandBuffer, vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
            mDatas[i].Lower.Barrier(commandBuffer, vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
            mDatas[i].B.Clear(commandBuffer);
        }

        std::size_t maxDepth = mDepth.GetMaxDepth();
        mMatrixBuildBound[maxDepth - 1].PushConstant(commandBuffer, 8, mDelta);
        mMatrixBuildBound[maxDepth - 1].Record(commandBuffer);
        mDatas[maxDepth - 1].Diagonal.Barrier(commandBuffer, vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
        mDatas[maxDepth - 1].Lower.Barrier(commandBuffer, vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
    });
}

void Multigrid::BindRecursive(Pressure& pressure, std::size_t depth)
{
    auto s0 = mDepth.GetDepthSize(depth);

    if (depth < mDepth.GetMaxDepth())
    {
        auto s1 = mDepth.GetDepthSize(depth + 1);
        mLiquidPhiScaleWorkBound.push_back(mPhiScaleWork.Bind(s1, {mLiquidPhis[depth - 1], mLiquidPhis[depth]}));
        mSolidPhiScaleWorkBound.push_back(mPhiScaleWork.Bind(s1, {mSolidPhis[depth - 1], mSolidPhis[depth]}));

        mResidualWorkBound[depth] =
                    mResidualWork.Bind(s0, {mDatas[depth-1].X,
                                           mDatas[depth-1].Diagonal,
                                           mDatas[depth-1].Lower,
                                           mDatas[depth-1].B,
                                           mResiduals[depth]});

        mTransfer.InitRestrict(depth, s0,
                               mResiduals[depth],
                               mDatas[depth-1].Diagonal,
                               mDatas[depth].B,
                               mDatas[depth].Diagonal);

        mTransfer.InitProlongate(depth, s0,
                                 mDatas[depth-1].X,
                                 mDatas[depth-1].Diagonal,
                                 mDatas[depth].X,
                                 mDatas[depth].Diagonal);

        BindRecursive(pressure, depth+1);
    }

    mMatrixBuildBound.push_back(
                pressure.BindMatrixBuild(s0,
                                         mDatas[depth-1].Diagonal,
                                         mDatas[depth-1].Lower,
                                         mLiquidPhis[depth-1],
                                         mSolidPhis[depth-1]));

    mSmoothers[2 * depth] =
          mDampedJacobi.Bind(s0, {mDatas[depth-1].X,
                                  mXs[depth],
                                  mDatas[depth-1].Diagonal,
                                  mDatas[depth-1].Lower,
                                  mDatas[depth-1].B});
    mSmoothers[2 * depth + 1] =
          mDampedJacobi.Bind(s0, {mXs[depth],
                                  mDatas[depth-1].X,
                                  mDatas[depth-1].Diagonal,
                                  mDatas[depth-1].Lower,
                                  mDatas[depth-1].B});
}

void Multigrid::BuildHierarchies()
{
    mBuildHierarchies.Submit();
}

void Multigrid::Smoother(vk::CommandBuffer commandBuffer, int n, int iterations)
{
  float w = 2.0f / 3.0f;
  int level = n * 2;
  for (int i = 0; i < iterations; i++)
  {
    mSmoothers[level].PushConstant(commandBuffer, 8, w);
    mSmoothers[level].Record(commandBuffer);
    mXs[n].Barrier(commandBuffer, vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
    mSmoothers[level + 1].PushConstant(commandBuffer, 8, w);
    mSmoothers[level + 1].Record(commandBuffer);

    if (n == 0) mPressure->Barrier(commandBuffer, vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
    else mDatas[n - 1].X.Barrier(commandBuffer, vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
  }
}

void Multigrid::Record(vk::CommandBuffer commandBuffer)
{
    const int numIterations = 2;

    if (mEnableStatistics) mStatistics.Start(commandBuffer);

    assert(mPressure != nullptr);
    mPressure->Clear(commandBuffer);
    mXs[0].Clear(commandBuffer);

    if (mEnableStatistics) mStatistics.Tick(commandBuffer, "clear");

    for (int i = 0; i < mDepth.GetMaxDepth(); i++)
    {
        Smoother(commandBuffer, i, numIterations);
        if (mEnableStatistics) mStatistics.Tick(commandBuffer, "smoother " + std::to_string(i));

        mResidualWorkBound[i].Record(commandBuffer);
        mResiduals[i].Barrier(commandBuffer, vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
        if (mEnableStatistics) mStatistics.Tick(commandBuffer, "residual " + std::to_string(i));

        mTransfer.Restrict(commandBuffer, i);
        if (mEnableStatistics) mStatistics.Tick(commandBuffer, "transfer " + std::to_string(i));

        mDatas[i].X.Clear(commandBuffer);
        mXs[i-1].Clear(commandBuffer);
        if (mEnableStatistics) mStatistics.Tick(commandBuffer, "clear " + std::to_string(i));

    }

    Smoother(commandBuffer, mDepth.GetMaxDepth(), numIterations);
    if (mEnableStatistics) mStatistics.Tick(commandBuffer, "smoother max");

    for (int i = mDepth.GetMaxDepth() - 1; i >= 0; --i)
    {
        mTransfer.Prolongate(commandBuffer, i);
        if (mEnableStatistics) mStatistics.Tick(commandBuffer, "prolongate " + std::to_string(i));

        Smoother(commandBuffer, i, numIterations);
        if (mEnableStatistics) mStatistics.Tick(commandBuffer, "smoother " + std::to_string(i));
    }

    if (mEnableStatistics) mStatistics.End(commandBuffer, "end");
}

Renderer::Statistics::Timestamps Multigrid::GetStatistics()
{
    return mStatistics.GetTimestamps();
}

}}
<commit_msg>fix typo<commit_after>//
//  Multigrid.cpp
//  Vortex2D
//

#include "Multigrid.h"

#include <Vortex2D/Engine/Pressure.h>

namespace Vortex2D { namespace Fluid {

Depth::Depth(const glm::ivec2& size)
{
    auto s = size;
    mDepths.push_back(s);

    const float min_size = 16.0f;
    while (s.x > min_size && s.y > min_size)
    {
        if (s.x % 2 != 0 || s.y % 2 != 0) throw std::runtime_error("Invalid multigrid size");

        s = s / glm::ivec2(2);
        mDepths.push_back(s);
    }
}

int Depth::GetMaxDepth() const
{
    return mDepths.size() - 1;
}

glm::ivec2 Depth::GetDepthSize(int i) const
{
    assert(i < mDepths.size());
    return mDepths[i];
}

Multigrid::Multigrid(const Renderer::Device& device, const glm::ivec2& size, float delta, bool statistics)
    : mDepth(size)
    , mDelta(delta)
    , mResidualWork(device, size, "../Vortex2D/Residual.comp.spv")
    , mTransfer(device)
    , mPhiScaleWork(device, size, "../Vortex2D/PhiScale.comp.spv")
    , mDampedJacobi(device, size, "../Vortex2D/DampedJacobi.comp.spv")
    , mBuildHierarchies(device, false)
    , mEnableStatistics(statistics)
    , mStatistics(device)
{
    for (int i = 1; i <= mDepth.GetMaxDepth(); i++)
    {
        auto s = mDepth.GetDepthSize(i);
        mDatas.emplace_back(device, s);

        mSolidPhis.emplace_back(device, s);
        mLiquidPhis.emplace_back(device, s);

        mLiquidPhis.back().ExtrapolateInit(mSolidPhis.back());
    }

    for (int i = 0; i < mDepth.GetMaxDepth(); i++)
    {
        auto s = mDepth.GetDepthSize(i);
        mResiduals.emplace_back(device, s.x*s.y);
    }

    for (int i = 0; i <= mDepth.GetMaxDepth(); i++)
    {
        auto s = mDepth.GetDepthSize(i);
        mXs.emplace_back(device, s.x*s.y);
    }

    mSmoothers.resize(2 * (mDepth.GetMaxDepth() + 1));
    mResidualWorkBound.resize(mDepth.GetMaxDepth() + 1);
}

void Multigrid::Init(Renderer::GenericBuffer& d,
                     Renderer::GenericBuffer& l,
                     Renderer::GenericBuffer& b,
                     Renderer::GenericBuffer& pressure)

{
    mPressure = &pressure;

    mResidualWorkBound[0] =
                mResidualWork.Bind({pressure, d, l, b, mResiduals[0]});
    mSmoothers[0] = mDampedJacobi.Bind({pressure, mXs[0], d, l, b});
    mSmoothers[1] = mDampedJacobi.Bind({mXs[0], pressure, d, l, b});

    auto s = mDepth.GetDepthSize(0);
    mTransfer.InitRestrict(0, s, mResiduals[0], d, mDatas[0].B, mDatas[0].Diagonal);
    mTransfer.InitProlongate(0, s, pressure, d, mDatas[0].X, mDatas[0].Diagonal);
}

void Multigrid::BuildHierarchiesInit(Pressure& pressure,
                                     Renderer::Texture& solidPhi,
                                     Renderer::Texture& liquidPhi)
{
    auto s = mDepth.GetDepthSize(1);
    mLiquidPhiScaleWorkBound.push_back(mPhiScaleWork.Bind(s, {liquidPhi, mLiquidPhis[0]}));
    mSolidPhiScaleWorkBound.push_back(mPhiScaleWork.Bind(s, {solidPhi, mSolidPhis[0]}));

    BindRecursive(pressure, 1);

    mBuildHierarchies.Record([&](vk::CommandBuffer commandBuffer)
    {
        for (std::size_t i = 0; i < mDepth.GetMaxDepth(); i++)
        {
            mLiquidPhiScaleWorkBound[i].Record(commandBuffer);
            mLiquidPhis[i].Barrier(commandBuffer,
                                   vk::ImageLayout::eGeneral,
                                   vk::AccessFlagBits::eShaderWrite,
                                   vk::ImageLayout::eGeneral,
                                   vk::AccessFlagBits::eShaderRead);

            mSolidPhiScaleWorkBound[i].Record(commandBuffer);
            mSolidPhis[i].Barrier(commandBuffer,
                                  vk::ImageLayout::eGeneral,
                                  vk::AccessFlagBits::eShaderWrite,
                                  vk::ImageLayout::eGeneral,
                                  vk::AccessFlagBits::eShaderRead);

            mLiquidPhis[i].ExtrapolateRecord(commandBuffer);

            mMatrixBuildBound[i].PushConstant(commandBuffer, 8, mDelta);
            mMatrixBuildBound[i].Record(commandBuffer);
            mDatas[i].Diagonal.Barrier(commandBuffer, vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
            mDatas[i].Lower.Barrier(commandBuffer, vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
            mDatas[i].B.Clear(commandBuffer);
        }

        std::size_t maxDepth = mDepth.GetMaxDepth();
        mMatrixBuildBound[maxDepth - 1].PushConstant(commandBuffer, 8, mDelta);
        mMatrixBuildBound[maxDepth - 1].Record(commandBuffer);
        mDatas[maxDepth - 1].Diagonal.Barrier(commandBuffer, vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
        mDatas[maxDepth - 1].Lower.Barrier(commandBuffer, vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
    });
}

void Multigrid::BindRecursive(Pressure& pressure, std::size_t depth)
{
    auto s0 = mDepth.GetDepthSize(depth);

    if (depth < mDepth.GetMaxDepth())
    {
        auto s1 = mDepth.GetDepthSize(depth + 1);
        mLiquidPhiScaleWorkBound.push_back(mPhiScaleWork.Bind(s1, {mLiquidPhis[depth - 1], mLiquidPhis[depth]}));
        mSolidPhiScaleWorkBound.push_back(mPhiScaleWork.Bind(s1, {mSolidPhis[depth - 1], mSolidPhis[depth]}));

        mResidualWorkBound[depth] =
                    mResidualWork.Bind(s0, {mDatas[depth-1].X,
                                           mDatas[depth-1].Diagonal,
                                           mDatas[depth-1].Lower,
                                           mDatas[depth-1].B,
                                           mResiduals[depth]});

        mTransfer.InitRestrict(depth, s0,
                               mResiduals[depth],
                               mDatas[depth-1].Diagonal,
                               mDatas[depth].B,
                               mDatas[depth].Diagonal);

        mTransfer.InitProlongate(depth, s0,
                                 mDatas[depth-1].X,
                                 mDatas[depth-1].Diagonal,
                                 mDatas[depth].X,
                                 mDatas[depth].Diagonal);

        BindRecursive(pressure, depth+1);
    }

    mMatrixBuildBound.push_back(
                pressure.BindMatrixBuild(s0,
                                         mDatas[depth-1].Diagonal,
                                         mDatas[depth-1].Lower,
                                         mLiquidPhis[depth-1],
                                         mSolidPhis[depth-1]));

    mSmoothers[2 * depth] =
          mDampedJacobi.Bind(s0, {mDatas[depth-1].X,
                                  mXs[depth],
                                  mDatas[depth-1].Diagonal,
                                  mDatas[depth-1].Lower,
                                  mDatas[depth-1].B});
    mSmoothers[2 * depth + 1] =
          mDampedJacobi.Bind(s0, {mXs[depth],
                                  mDatas[depth-1].X,
                                  mDatas[depth-1].Diagonal,
                                  mDatas[depth-1].Lower,
                                  mDatas[depth-1].B});
}

void Multigrid::BuildHierarchies()
{
    mBuildHierarchies.Submit();
}

void Multigrid::Smoother(vk::CommandBuffer commandBuffer, int n, int iterations)
{
  float w = 2.0f / 3.0f;
  int level = n * 2;
  for (int i = 0; i < iterations; i++)
  {
    mSmoothers[level].PushConstant(commandBuffer, 8, w);
    mSmoothers[level].Record(commandBuffer);
    mXs[n].Barrier(commandBuffer, vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
    mSmoothers[level + 1].PushConstant(commandBuffer, 8, w);
    mSmoothers[level + 1].Record(commandBuffer);

    if (n == 0) mPressure->Barrier(commandBuffer, vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
    else mDatas[n - 1].X.Barrier(commandBuffer, vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
  }
}

void Multigrid::Record(vk::CommandBuffer commandBuffer)
{
    const int numIterations = 2;

    if (mEnableStatistics) mStatistics.Start(commandBuffer);

    assert(mPressure != nullptr);
    mPressure->Clear(commandBuffer);
    mXs[0].Clear(commandBuffer);

    if (mEnableStatistics) mStatistics.Tick(commandBuffer, "clear");

    for (int i = 0; i < mDepth.GetMaxDepth(); i++)
    {
        Smoother(commandBuffer, i, numIterations);
        if (mEnableStatistics) mStatistics.Tick(commandBuffer, "smoother " + std::to_string(i));

        mResidualWorkBound[i].Record(commandBuffer);
        mResiduals[i].Barrier(commandBuffer, vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
        if (mEnableStatistics) mStatistics.Tick(commandBuffer, "residual " + std::to_string(i));

        mTransfer.Restrict(commandBuffer, i);
        if (mEnableStatistics) mStatistics.Tick(commandBuffer, "transfer " + std::to_string(i));

        mDatas[i].X.Clear(commandBuffer);
        mXs[i].Clear(commandBuffer);
        if (mEnableStatistics) mStatistics.Tick(commandBuffer, "clear " + std::to_string(i));

    }

    Smoother(commandBuffer, mDepth.GetMaxDepth(), numIterations);
    if (mEnableStatistics) mStatistics.Tick(commandBuffer, "smoother max");

    for (int i = mDepth.GetMaxDepth() - 1; i >= 0; --i)
    {
        mTransfer.Prolongate(commandBuffer, i);
        if (mEnableStatistics) mStatistics.Tick(commandBuffer, "prolongate " + std::to_string(i));

        Smoother(commandBuffer, i, numIterations);
        if (mEnableStatistics) mStatistics.Tick(commandBuffer, "smoother " + std::to_string(i));
    }

    if (mEnableStatistics) mStatistics.End(commandBuffer, "end");
}

Renderer::Statistics::Timestamps Multigrid::GetStatistics()
{
    return mStatistics.GetTimestamps();
}

}}
<|endoftext|>


<commit_before>#include "src/meta/zp_meta_election.h"

#include <glog/logging.h>
#include "slash/include/env.h"
#include "slash/include/slash_string.h"

#include "include/zp_const.h"
#include "include/zp_conf.h"

extern ZpConf* g_zp_conf;
const std::string kElectLockKey = "##elect_lock";
const std::string kLeaderKey = "##meta_leader11111";

static bool IsLeaderTimeout(uint64_t last_active, uint64_t timeout) {
  return last_active + timeout * 1000 > slash::NowMicros();
}

ZPMetaElection::ZPMetaElection(floyd::Floyd* f)
  : floyd_(f) {
  }

ZPMetaElection::~ZPMetaElection() {
}

Status ZPMetaElection::ReadLeaderRecord(ZPMeta::MetaLeader* cleader) {
  std::string value;
  Status s = floyd_->Read(kLeaderKey, &value);
  if (!s.ok()) {
    LOG(WARNING) << "Read Leader Key failed: " << s.ToString();
    return s;
  }
  if (!cleader->ParseFromString(value)) {
    LOG(WARNING) << "Parse MetaLeader failed," << value << "###";
    return Status::Corruption("Parse MetaLeader failed");
  }
  return Status::OK();
}

Status ZPMetaElection::WriteLeaderRecord(const ZPMeta::MetaLeader& cleader) {
  std::string value;
  // Write back
  if (!cleader.SerializeToString(&value)) {
    LOG(WARNING) << "SerializeToString ZPMeta::MetaLeader failed.";
    return Status::Corruption("Serialize MetaLeader failed");
  }
  Status s = floyd_->Write(kLeaderKey, value);
  if (!s.ok()) {
    LOG(WARNING) << "Write MetaLeader failed." << s.ToString();
    return s;
  }
  return Status::OK();
}

bool ZPMetaElection::Jeopardy(std::string* ip, int* port) {
  if (!last_leader_.IsInitialized()  // no last
      || IsLeaderTimeout(last_leader_.last_active(),  // timeout
        kNodeCronInterval * kNodeCronWaitCount)) {
    return false;
  }
  *ip = last_leader_.leader().ip();
  *port = last_leader_.leader().port();
  return true;
}

// Check leader ip and port
// return false means faled to check leader for some time
bool ZPMetaElection::GetLeader(std::string* ip, int* port) {
  std::string local_ip = g_zp_conf->local_ip();
  int local_port = g_zp_conf->local_port();
  std::string mine = slash::IpPortString(local_ip, local_port);

  // Read first to avoid follower locking everytime
  ZPMeta::MetaLeader cleader;
  Status s = ReadLeaderRecord(&cleader);
  if (!s.ok() && !s.IsNotFound()) {
    return Jeopardy(ip, port);
  } else if (s.ok()) {
    last_leader_.CopyFrom(cleader);
    if ((local_ip != cleader.leader().ip()
          || local_port != cleader.leader().port())
        && !IsLeaderTimeout(cleader.last_active(), kMetaLeaderTimeout)) {
      // I'm not leader and current leader is not timeout
      *ip = cleader.leader().ip();
      *port = cleader.leader().port();
      return true;
    }
  }

  // Lock and update
  s = floyd_->TryLock(kElectLockKey, mine,
      kMetaLeaderLockTimeout * 1000);
  if (!s.ok()) {
    LOG(WARNING) << "TryLock ElectLock failed." << s.ToString();
    return Jeopardy(ip, port);
  }

  cleader.Clear();
  s = ReadLeaderRecord(&cleader);
  if (!s.ok() && !s.IsNotFound()) {
    LOG(WARNING) << "ReadLeaderRecord after lock failed, Jeopardy then. Error:"
      << s.ToString();
    floyd_->UnLock(kElectLockKey, mine);
    return Jeopardy(ip, port);
  } else if (s.ok()) {
    last_leader_.CopyFrom(cleader);
  }

  // 1. NotFound
  // 2, Ok, leader need refresh timeout
  // 3, Ok, follwer try elect
  if (s.IsNotFound()  // No leader yet
      || (cleader.leader().ip() == local_ip
        && cleader.leader().port() == local_port)     // I'm Leader
      || IsLeaderTimeout(cleader.last_active(),
        kMetaLeaderTimeout)) {  // Old leader timeout
    // Update
    cleader.mutable_leader()->set_ip(local_ip);
    cleader.mutable_leader()->set_port(local_port);
    cleader.set_last_active(slash::NowMicros());

    s = WriteLeaderRecord(cleader);
    if (s.ok()) {
      // Refresh cache
      last_leader_.CopyFrom(cleader);
    }
  }

  *ip = cleader.leader().ip();
  *port = cleader.leader().port();
  floyd_->UnLock(kElectLockKey, mine);
  return true;
}
<commit_msg>fix meta election jeopardy timeout too small problem<commit_after>#include "src/meta/zp_meta_election.h"

#include <glog/logging.h>
#include "slash/include/env.h"
#include "slash/include/slash_string.h"

#include "include/zp_const.h"
#include "include/zp_conf.h"

extern ZpConf* g_zp_conf;
const std::string kElectLockKey = "##elect_lock";
const std::string kLeaderKey = "##meta_leader11111";

static bool IsLeaderTimeout(uint64_t last_active, uint64_t timeout) {
  return last_active + timeout * 1000 > slash::NowMicros();
}

ZPMetaElection::ZPMetaElection(floyd::Floyd* f)
  : floyd_(f) {
  }

ZPMetaElection::~ZPMetaElection() {
}

Status ZPMetaElection::ReadLeaderRecord(ZPMeta::MetaLeader* cleader) {
  std::string value;
  Status s = floyd_->Read(kLeaderKey, &value);
  if (!s.ok()) {
    LOG(WARNING) << "Read Leader Key failed: " << s.ToString();
    return s;
  }
  if (!cleader->ParseFromString(value)) {
    LOG(WARNING) << "Parse MetaLeader failed," << value << "###";
    return Status::Corruption("Parse MetaLeader failed");
  }
  return Status::OK();
}

Status ZPMetaElection::WriteLeaderRecord(const ZPMeta::MetaLeader& cleader) {
  std::string value;
  // Write back
  if (!cleader.SerializeToString(&value)) {
    LOG(WARNING) << "SerializeToString ZPMeta::MetaLeader failed.";
    return Status::Corruption("Serialize MetaLeader failed");
  }
  Status s = floyd_->Write(kLeaderKey, value);
  if (!s.ok()) {
    LOG(WARNING) << "Write MetaLeader failed." << s.ToString();
    return s;
  }
  return Status::OK();
}

bool ZPMetaElection::Jeopardy(std::string* ip, int* port) {
  if (!last_leader_.IsInitialized()) {
    // no last
    LOG(WARNING) << "Jeopardy finished since no last leader";
    return false;
  }
  uint64_t timeout = kMetaLeaderTimeout;
  if (last_leader_.leader().ip() == g_zp_conf->local_ip()
      && last_leader_.leader().port() == g_zp_conf->local_port()) {
    // Smaller timeout for leader so that it could give up leadership on time
    // before any follower think it could be leader
    timeout -= kMetaLeaderRemainThreshold;
  }
  if (IsLeaderTimeout(last_leader_.last_active(), timeout)) {
    LOG(WARNING) << "Jeopardy finished since timeout";
    return false;
  }
  *ip = last_leader_.leader().ip();
  *port = last_leader_.leader().port();
  return true;
}

// Check leader ip and port
// return false means faled to check leader for some time
bool ZPMetaElection::GetLeader(std::string* ip, int* port) {
  std::string local_ip = g_zp_conf->local_ip();
  int local_port = g_zp_conf->local_port();
  std::string mine = slash::IpPortString(local_ip, local_port);

  // Read first to avoid follower locking everytime
  ZPMeta::MetaLeader cleader;
  Status s = ReadLeaderRecord(&cleader);
  if (!s.ok() && !s.IsNotFound()) {
    LOG(WARNING) << "pre-ReadLeaderRecord failed: " << s.ToString()
      << ", check jeopardy";
    return Jeopardy(ip, port);
  } else if (s.ok()) {
    last_leader_.CopyFrom(cleader);
    if ((local_ip != cleader.leader().ip()
          || local_port != cleader.leader().port())
        && !IsLeaderTimeout(cleader.last_active(), kMetaLeaderTimeout)) {
      // I'm not leader and current leader is not timeout
      *ip = cleader.leader().ip();
      *port = cleader.leader().port();
      return true;
    }
  }

  // Lock and update
  s = floyd_->TryLock(kElectLockKey, mine,
      kMetaLeaderLockTimeout * 1000);
  if (!s.ok()) {
    LOG(WARNING) << "TryLock ElectLock failed." << s.ToString();
    return Jeopardy(ip, port);
  }

  cleader.Clear();
  s = ReadLeaderRecord(&cleader);
  if (!s.ok() && !s.IsNotFound()) {
    LOG(WARNING) << "ReadLeaderRecord after lock failed, Jeopardy then. Error:"
      << s.ToString();
    floyd_->UnLock(kElectLockKey, mine);
    LOG(WARNING) << "ReadLeaderRecord failed: " << s.ToString()
      << ", check jeopardy";
    return Jeopardy(ip, port);
  } else if (s.ok()) {
    last_leader_.CopyFrom(cleader);
  }

  // 1. NotFound
  // 2, Ok, leader need refresh timeout
  // 3, Ok, follwer try elect
  if (s.IsNotFound()  // No leader yet
      || (cleader.leader().ip() == local_ip
        && cleader.leader().port() == local_port)     // I'm Leader
      || IsLeaderTimeout(cleader.last_active(),
        kMetaLeaderTimeout)) {  // Old leader timeout
    if (cleader.leader().ip() != local_ip
        || cleader.leader().port() != local_port) {
      LOG(INFO) << "Take over the leadership, since: "
        << (s.IsNotFound() ? "no leader record" : "old leader timeout");
    }

    // Update
    cleader.mutable_leader()->set_ip(local_ip);
    cleader.mutable_leader()->set_port(local_port);
    cleader.set_last_active(slash::NowMicros());

    s = WriteLeaderRecord(cleader);
    if (s.ok()) {
      // Refresh cache
      last_leader_.CopyFrom(cleader);
    }
  }

  *ip = cleader.leader().ip();
  *port = cleader.leader().port();
  floyd_->UnLock(kElectLockKey, mine);
  return true;
}
<|endoftext|>


<commit_before>/* This file is part of the Pangolin Project.
 * http://github.com/stevenlovegrove/Pangolin
 *
 * Copyright (c) 2015 Steven Lovegrove
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include <pangolin/video/drivers/pleora.h>

namespace pangolin
{

template<typename T>
struct PleoraParamTraits;

template<> struct PleoraParamTraits<bool> {
    typedef PvGenBoolean PvType;
};
template<> struct PleoraParamTraits<int64_t> {
    typedef PvGenInteger PvType;
};
template<> struct PleoraParamTraits<float> {
    typedef PvGenFloat PvType;
};
template<> struct PleoraParamTraits<std::string> {
    typedef PvGenString PvType;
};

template<typename T>
T GetParam(PvGenParameterArray* params, const char* name)
{
    typedef typename PleoraParamTraits<T>::PvType PvType;
    PvType* param = dynamic_cast<PvType*>( params->Get(name) );
    if(!param) {
        throw std::runtime_error("Incorrect type");
    }
    T ret;
    PvResult res = param->GetValue(ret);
    if(res.IsFailure()) {
        throw std::runtime_error(res.GetCodeString().GetAscii());
    }
    return ret;
}

inline const PvDeviceInfo* SelectDevice( PvSystem& aSystem, const char* model_name = 0, const char* serial_num = 0, size_t index = 0 )
{
    aSystem.Find();

    // Enumerate all devices, select first that matches criteria
    size_t matches = 0;
    for ( uint32_t i = 0; i < aSystem.GetInterfaceCount(); i++ ) {
        const PvInterface *lInterface = dynamic_cast<const PvInterface *>( aSystem.GetInterface( i ) );
        if ( lInterface ) {
            for ( uint32_t j = 0; j < lInterface->GetDeviceCount(); j++ ) {
                const PvDeviceInfo *lDI = dynamic_cast<const PvDeviceInfo *>( lInterface->GetDeviceInfo( j ) );
                if ( lDI && lDI->IsConfigurationValid() ) {
                    if( model_name && strcmp(lDI->GetModelName().GetAscii(), model_name) )
                        continue;
                    if( serial_num && strcmp(lDI->GetSerialNumber().GetAscii(), serial_num) )
                        continue;
                    if(matches == index) {
                        return lDI;
                    }
                    ++matches;
                }
            }
        }
    }

    return 0;
}

VideoPixelFormat PleoraFormat(const PvGenEnum* pfmt)
{
    std::string spfmt = pfmt->ToString().GetAscii();
    if( !spfmt.compare("Mono8") ) {
        return VideoFormatFromString("GRAY8");
    }else if( !spfmt.compare("Mono10p") ) {
        return VideoFormatFromString("GRAY10");
    }else if( !spfmt.compare("Mono12p") ) {
        return VideoFormatFromString("GRAY12");
    }else{
        throw VideoException("Unknown Pleora pixel format", spfmt);
    }
}

PleoraVideo::PleoraVideo(const char* model_name, const char* serial_num, size_t index, size_t bpp, size_t buffer_count)
    : lPvSystem(0), lDevice(0), lStream(0)
{
    lPvSystem = new PvSystem();
    if ( !lPvSystem ) {
        throw pangolin::VideoException("Pleora: Unable to create PvSystem");
    }

    const PvDeviceInfo *lDeviceInfo = SelectDevice(*lPvSystem, model_name, serial_num, index);
    if ( !lDeviceInfo ) {
        throw pangolin::VideoException("Pleora: Unable to select device");
    }

    PvResult lResult;
    lDevice = PvDevice::CreateAndConnect( lDeviceInfo, &lResult );
    if ( !lDevice ) {
        throw pangolin::VideoException("Pleora: Unable to connect to device", lResult.GetDescription().GetAscii() );
    }

    lStream = PvStream::CreateAndOpen( lDeviceInfo->GetConnectionID(), &lResult );
    if ( !lStream ) {
        throw pangolin::VideoException("Pleora: Unable to open stream", lResult.GetDescription().GetAscii() );
    }

    lDeviceParams = lDevice->GetParameters();
    lStart = dynamic_cast<PvGenCommand*>( lDeviceParams->Get( "AcquisitionStart" ) );
    lStop = dynamic_cast<PvGenCommand*>( lDeviceParams->Get( "AcquisitionStop" ) );

    if( bpp == 8) {
        lDeviceParams->SetEnumValue("PixelFormat", PvString("Mono8") );
    }else if(bpp == 10) {
        lDeviceParams->SetEnumValue("PixelFormat", PvString("Mono10p") );
    }else if(bpp == 12) {
        lDeviceParams->SetEnumValue("PixelFormat", PvString("Mono12p") );
    }

    lStreamParams = lStream->GetParameters();

    // Reading payload size from device
    const uint32_t lSize = lDevice->GetPayloadSize();

    // Use buffer_count or the maximum number of buffers, whichever is smaller
    const uint32_t lBufferCount = ( lStream->GetQueuedBufferMaximum() < buffer_count ) ?
        lStream->GetQueuedBufferMaximum() :
        buffer_count;

    // Allocate buffers and queue
    for ( uint32_t i = 0; i < lBufferCount; i++ )
    {
        PvBuffer *lBuffer = new PvBuffer;
        lBuffer->Alloc( static_cast<uint32_t>( lSize ) );
        lBufferList.push_back( lBuffer );
    }

    const int w = DeviceParam<int64_t>("Width");
    const int h = DeviceParam<int64_t>("Height");

    // Setup pangolin for stream
    PvGenEnum* lpixfmt = dynamic_cast<PvGenEnum*>( lDeviceParams->Get("PixelFormat") );
    const VideoPixelFormat fmt = PleoraFormat(lpixfmt);
    streams.push_back(StreamInfo(fmt, w, h, (w*fmt.bpp)/8));
    size_bytes = lSize;

    Start();
}

PleoraVideo::~PleoraVideo()
{
    Stop();

    // Free buffers
    for( BufferList::iterator lIt = lBufferList.begin(); lIt != lBufferList.end(); lIt++ ) {
        delete *lIt;
    }

    if(lStream) {
        lStream->Close();
        PvStream::Free( lStream );
    }

    if(lDevice) {
        lDevice->Disconnect();
        PvDevice::Free( lDevice );
    }

    delete lPvSystem;
}

void PleoraVideo::Start()
{
    // Queue all buffers in the stream
    for( BufferList::iterator lIt = lBufferList.begin(); lIt != lBufferList.end(); lIt++ ) {
        lStream->QueueBuffer( *lIt );
    }

    lDevice->StreamEnable();
    lStart->Execute();
}

void PleoraVideo::Stop()
{
    lStop->Execute();
    lDevice->StreamDisable();

    // Abort all buffers from the stream and dequeue
    lStream->AbortQueuedBuffers();
    while ( lStream->GetQueuedBufferCount() > 0 )
    {
        PvBuffer *lBuffer = NULL;
        PvResult lOperationResult;
        lStream->RetrieveBuffer( &lBuffer, &lOperationResult );
    }
}

size_t PleoraVideo::SizeBytes() const
{
    return size_bytes;
}

const std::vector<StreamInfo>& PleoraVideo::Streams() const
{
    return streams;
}

bool PleoraVideo::GrabNext( unsigned char* image, bool /*wait*/ )
{
    PvBuffer *lBuffer = NULL;
    PvResult lOperationResult;

    // Retrieve next buffer
    PvResult lResult = lStream->RetrieveBuffer( &lBuffer, &lOperationResult, 1000 );
    if ( !lResult.IsOK() ) {
        pango_print_warn("Pleora error: %s\n", lResult.GetCodeString().GetAscii() );
        return false;
    }

    bool good = false;

    if ( lOperationResult.IsOK() )
    {
        PvPayloadType lType = lBuffer->GetPayloadType();
        if ( lType == PvPayloadTypeImage )
        {
            PvImage *lImage = lBuffer->GetImage();
            std::memcpy(image, lImage->GetDataPointer(), size_bytes);
            good = true;
        }
    } else {
        pango_print_warn("Pleora error: %s\n", lOperationResult.GetCodeString().GetAscii() );
    }

    lStream->QueueBuffer( lBuffer );
    return good;
}

bool PleoraVideo::GrabNewest( unsigned char* image, bool wait )
{
    PvBuffer *lBuffer0 = NULL;
    PvBuffer *lBuffer = NULL;
    PvResult lOperationResult;

    const uint32_t timeout = wait ? 0xFFFFFFFF : 0;

    PvResult lResult = lStream->RetrieveBuffer( &lBuffer, &lOperationResult, timeout );
    if ( !lResult.IsOK() ) {
        pango_print_warn("Pleora error: %s\n", lResult.GetCodeString().GetAscii() );
        return false;
    }else if( !lOperationResult.IsOK() ) {
        pango_print_warn("Pleora error: %s\n", lOperationResult.GetCodeString().GetAscii() );
        lStream->QueueBuffer( lBuffer );
        return false;
    }

    // We have at least one frame. Capture more until we fail, 0 timeout
    while(true) {
        PvResult lResult = lStream->RetrieveBuffer( &lBuffer0, &lOperationResult, 0 );
        if ( !lResult.IsOK() ) {
            break;
        }else if( !lOperationResult.IsOK() ) {
            lStream->QueueBuffer( lBuffer0 );
            break;
        }else{
            lStream->QueueBuffer( lBuffer );
            lBuffer = lBuffer0;
        }
    }

    bool good = false;

    PvPayloadType lType = lBuffer->GetPayloadType();
    if ( lType == PvPayloadTypeImage )
    {
        PvImage *lImage = lBuffer->GetImage();
        std::memcpy(image, lImage->GetDataPointer(), size_bytes);
        good = true;
    }

    lStream->QueueBuffer( lBuffer );
    return good;
}

template<typename T>
T PleoraVideo::DeviceParam(const char* name)
{
    return GetParam<T>(lDeviceParams, name);
}

template<typename T>
T PleoraVideo::StreamParam(const char* name)
{
    return GetParam<T>(lStreamParams, name);
}

}
<commit_msg>Pleora: Free lDevice if stream fails to open.<commit_after>/* This file is part of the Pangolin Project.
 * http://github.com/stevenlovegrove/Pangolin
 *
 * Copyright (c) 2015 Steven Lovegrove
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include <pangolin/video/drivers/pleora.h>

namespace pangolin
{

template<typename T>
struct PleoraParamTraits;

template<> struct PleoraParamTraits<bool> {
    typedef PvGenBoolean PvType;
};
template<> struct PleoraParamTraits<int64_t> {
    typedef PvGenInteger PvType;
};
template<> struct PleoraParamTraits<float> {
    typedef PvGenFloat PvType;
};
template<> struct PleoraParamTraits<std::string> {
    typedef PvGenString PvType;
};

template<typename T>
T GetParam(PvGenParameterArray* params, const char* name)
{
    typedef typename PleoraParamTraits<T>::PvType PvType;
    PvType* param = dynamic_cast<PvType*>( params->Get(name) );
    if(!param) {
        throw std::runtime_error("Incorrect type");
    }
    T ret;
    PvResult res = param->GetValue(ret);
    if(res.IsFailure()) {
        throw std::runtime_error(res.GetCodeString().GetAscii());
    }
    return ret;
}

inline const PvDeviceInfo* SelectDevice( PvSystem& aSystem, const char* model_name = 0, const char* serial_num = 0, size_t index = 0 )
{
    aSystem.Find();

    // Enumerate all devices, select first that matches criteria
    size_t matches = 0;
    for ( uint32_t i = 0; i < aSystem.GetInterfaceCount(); i++ ) {
        const PvInterface *lInterface = dynamic_cast<const PvInterface *>( aSystem.GetInterface( i ) );
        if ( lInterface ) {
            for ( uint32_t j = 0; j < lInterface->GetDeviceCount(); j++ ) {
                const PvDeviceInfo *lDI = dynamic_cast<const PvDeviceInfo *>( lInterface->GetDeviceInfo( j ) );
                if ( lDI && lDI->IsConfigurationValid() ) {
                    if( model_name && strcmp(lDI->GetModelName().GetAscii(), model_name) )
                        continue;
                    if( serial_num && strcmp(lDI->GetSerialNumber().GetAscii(), serial_num) )
                        continue;
                    if(matches == index) {
                        return lDI;
                    }
                    ++matches;
                }
            }
        }
    }

    return 0;
}

VideoPixelFormat PleoraFormat(const PvGenEnum* pfmt)
{
    std::string spfmt = pfmt->ToString().GetAscii();
    if( !spfmt.compare("Mono8") ) {
        return VideoFormatFromString("GRAY8");
    }else if( !spfmt.compare("Mono10p") ) {
        return VideoFormatFromString("GRAY10");
    }else if( !spfmt.compare("Mono12p") ) {
        return VideoFormatFromString("GRAY12");
    }else{
        throw VideoException("Unknown Pleora pixel format", spfmt);
    }
}

PleoraVideo::PleoraVideo(const char* model_name, const char* serial_num, size_t index, size_t bpp, size_t buffer_count)
    : lPvSystem(0), lDevice(0), lStream(0)
{
    lPvSystem = new PvSystem();
    if ( !lPvSystem ) {
        throw pangolin::VideoException("Pleora: Unable to create PvSystem");
    }

    const PvDeviceInfo *lDeviceInfo = SelectDevice(*lPvSystem, model_name, serial_num, index);
    if ( !lDeviceInfo ) {
        throw pangolin::VideoException("Pleora: Unable to select device");
    }

    PvResult lResult;
    lDevice = PvDevice::CreateAndConnect( lDeviceInfo, &lResult );
    if ( !lDevice ) {
        throw pangolin::VideoException("Pleora: Unable to connect to device", lResult.GetDescription().GetAscii() );
    }

    lStream = PvStream::CreateAndOpen( lDeviceInfo->GetConnectionID(), &lResult );
    if ( !lStream ) {
        lDevice->Disconnect();
        PvDevice::Free(lDevice);
        throw pangolin::VideoException("Pleora: Unable to open stream", lResult.GetDescription().GetAscii() );
    }

    lDeviceParams = lDevice->GetParameters();
    lStart = dynamic_cast<PvGenCommand*>( lDeviceParams->Get( "AcquisitionStart" ) );
    lStop = dynamic_cast<PvGenCommand*>( lDeviceParams->Get( "AcquisitionStop" ) );

    if( bpp == 8) {
        lDeviceParams->SetEnumValue("PixelFormat", PvString("Mono8") );
    }else if(bpp == 10) {
        lDeviceParams->SetEnumValue("PixelFormat", PvString("Mono10p") );
    }else if(bpp == 12) {
        lDeviceParams->SetEnumValue("PixelFormat", PvString("Mono12p") );
    }

    lStreamParams = lStream->GetParameters();

    // Reading payload size from device
    const uint32_t lSize = lDevice->GetPayloadSize();

    // Use buffer_count or the maximum number of buffers, whichever is smaller
    const uint32_t lBufferCount = ( lStream->GetQueuedBufferMaximum() < buffer_count ) ?
        lStream->GetQueuedBufferMaximum() :
        buffer_count;

    // Allocate buffers and queue
    for ( uint32_t i = 0; i < lBufferCount; i++ )
    {
        PvBuffer *lBuffer = new PvBuffer;
        lBuffer->Alloc( static_cast<uint32_t>( lSize ) );
        lBufferList.push_back( lBuffer );
    }

    const int w = DeviceParam<int64_t>("Width");
    const int h = DeviceParam<int64_t>("Height");

    // Setup pangolin for stream
    PvGenEnum* lpixfmt = dynamic_cast<PvGenEnum*>( lDeviceParams->Get("PixelFormat") );
    const VideoPixelFormat fmt = PleoraFormat(lpixfmt);
    streams.push_back(StreamInfo(fmt, w, h, (w*fmt.bpp)/8));
    size_bytes = lSize;

    Start();
}

PleoraVideo::~PleoraVideo()
{
    Stop();

    // Free buffers
    for( BufferList::iterator lIt = lBufferList.begin(); lIt != lBufferList.end(); lIt++ ) {
        delete *lIt;
    }

    if(lStream) {
        lStream->Close();
        PvStream::Free( lStream );
    }

    if(lDevice) {
        lDevice->Disconnect();
        PvDevice::Free( lDevice );
    }

    delete lPvSystem;
}

void PleoraVideo::Start()
{
    // Queue all buffers in the stream
    for( BufferList::iterator lIt = lBufferList.begin(); lIt != lBufferList.end(); lIt++ ) {
        lStream->QueueBuffer( *lIt );
    }

    lDevice->StreamEnable();
    lStart->Execute();
}

void PleoraVideo::Stop()
{
    lStop->Execute();
    lDevice->StreamDisable();

    // Abort all buffers from the stream and dequeue
    lStream->AbortQueuedBuffers();
    while ( lStream->GetQueuedBufferCount() > 0 )
    {
        PvBuffer *lBuffer = NULL;
        PvResult lOperationResult;
        lStream->RetrieveBuffer( &lBuffer, &lOperationResult );
    }
}

size_t PleoraVideo::SizeBytes() const
{
    return size_bytes;
}

const std::vector<StreamInfo>& PleoraVideo::Streams() const
{
    return streams;
}

bool PleoraVideo::GrabNext( unsigned char* image, bool /*wait*/ )
{
    PvBuffer *lBuffer = NULL;
    PvResult lOperationResult;

    // Retrieve next buffer
    PvResult lResult = lStream->RetrieveBuffer( &lBuffer, &lOperationResult, 1000 );
    if ( !lResult.IsOK() ) {
        pango_print_warn("Pleora error: %s\n", lResult.GetCodeString().GetAscii() );
        return false;
    }

    bool good = false;

    if ( lOperationResult.IsOK() )
    {
        PvPayloadType lType = lBuffer->GetPayloadType();
        if ( lType == PvPayloadTypeImage )
        {
            PvImage *lImage = lBuffer->GetImage();
            std::memcpy(image, lImage->GetDataPointer(), size_bytes);
            good = true;
        }
    } else {
        pango_print_warn("Pleora error: %s\n", lOperationResult.GetCodeString().GetAscii() );
    }

    lStream->QueueBuffer( lBuffer );
    return good;
}

bool PleoraVideo::GrabNewest( unsigned char* image, bool wait )
{
    PvBuffer *lBuffer0 = NULL;
    PvBuffer *lBuffer = NULL;
    PvResult lOperationResult;

    const uint32_t timeout = wait ? 0xFFFFFFFF : 0;

    PvResult lResult = lStream->RetrieveBuffer( &lBuffer, &lOperationResult, timeout );
    if ( !lResult.IsOK() ) {
        pango_print_warn("Pleora error: %s\n", lResult.GetCodeString().GetAscii() );
        return false;
    }else if( !lOperationResult.IsOK() ) {
        pango_print_warn("Pleora error: %s\n", lOperationResult.GetCodeString().GetAscii() );
        lStream->QueueBuffer( lBuffer );
        return false;
    }

    // We have at least one frame. Capture more until we fail, 0 timeout
    while(true) {
        PvResult lResult = lStream->RetrieveBuffer( &lBuffer0, &lOperationResult, 0 );
        if ( !lResult.IsOK() ) {
            break;
        }else if( !lOperationResult.IsOK() ) {
            lStream->QueueBuffer( lBuffer0 );
            break;
        }else{
            lStream->QueueBuffer( lBuffer );
            lBuffer = lBuffer0;
        }
    }

    bool good = false;

    PvPayloadType lType = lBuffer->GetPayloadType();
    if ( lType == PvPayloadTypeImage )
    {
        PvImage *lImage = lBuffer->GetImage();
        std::memcpy(image, lImage->GetDataPointer(), size_bytes);
        good = true;
    }

    lStream->QueueBuffer( lBuffer );
    return good;
}

template<typename T>
T PleoraVideo::DeviceParam(const char* name)
{
    return GetParam<T>(lDeviceParams, name);
}

template<typename T>
T PleoraVideo::StreamParam(const char* name)
{
    return GetParam<T>(lStreamParams, name);
}

}
<|endoftext|>


<commit_before>/*ckwg +5
 * Copyright 2011-2012 by Kitware, Inc. All Rights Reserved. Please refer to
 * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel,
 * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065.
 */

#include "datum.h"

#include <sstream>

/**
 * \file datum.cxx
 *
 * \brief Implementation of a piece of \link vistk::datum data\endlink in the pipeline.
 */

namespace vistk
{

datum_t
datum::new_datum(boost::any const& dat)
{
  return datum_t(new datum(dat));
}

datum_t
datum
::empty_datum()
{
  return datum_t(new datum(empty));
}

datum_t
datum
::flush_datum()
{
  return datum_t(new datum(flush));
}

datum_t
datum
::complete_datum()
{
  return datum_t(new datum(complete));
}

datum_t
datum
::error_datum(error_t const& error)
{
  return datum_t(new datum(error));
}

datum::type_t
datum
::type() const
{
  return m_type;
}

datum::error_t
datum
::get_error() const
{
  return m_error;
}

datum
::datum(type_t ty)
  : m_type(ty)
  , m_error()
  , m_datum()
{
}

datum
::datum(error_t const& err)
  : m_type(error)
  , m_error(err)
  , m_datum()
{
}

datum
::datum(boost::any const& dat)
  : m_type(data)
  , m_error()
  , m_datum(dat)
{
}

datum_exception
::datum_exception() throw()
  : pipeline_exception()
{
}

datum_exception
::~datum_exception() throw()
{
}

bad_datum_cast_exception
::bad_datum_cast_exception(std::string const& typeid_, datum::type_t const& type, datum::error_t const& error, char const* reason) throw()
  : datum_exception()
  , m_typeid(typeid_)
  , m_type(type)
  , m_error(error)
  , m_reason(reason)
{
  std::ostringstream sstr;

  if (m_type == datum::error)
  {
    sstr << "Failed to cast datum of type "
            "\'" << m_type << "\' (" << m_error << ") into "
         << m_typeid << ": "
         << m_reason << ".";
  }
  else
  {
    sstr << "Failed to cast datum of type "
            "\'" << m_type << "\' into " << m_typeid << ": "
         << m_reason << ".";
  }

  m_what = sstr.str();
}

bad_datum_cast_exception
::~bad_datum_cast_exception() throw()
{
}

}
<commit_msg>Output strings instead of integers<commit_after>/*ckwg +5
 * Copyright 2011-2012 by Kitware, Inc. All Rights Reserved. Please refer to
 * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel,
 * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065.
 */

#include "datum.h"

#include <sstream>

/**
 * \file datum.cxx
 *
 * \brief Implementation of a piece of \link vistk::datum data\endlink in the pipeline.
 */

namespace vistk
{

datum_t
datum::new_datum(boost::any const& dat)
{
  return datum_t(new datum(dat));
}

datum_t
datum
::empty_datum()
{
  return datum_t(new datum(empty));
}

datum_t
datum
::flush_datum()
{
  return datum_t(new datum(flush));
}

datum_t
datum
::complete_datum()
{
  return datum_t(new datum(complete));
}

datum_t
datum
::error_datum(error_t const& error)
{
  return datum_t(new datum(error));
}

datum::type_t
datum
::type() const
{
  return m_type;
}

datum::error_t
datum
::get_error() const
{
  return m_error;
}

datum
::datum(type_t ty)
  : m_type(ty)
  , m_error()
  , m_datum()
{
}

datum
::datum(error_t const& err)
  : m_type(error)
  , m_error(err)
  , m_datum()
{
}

datum
::datum(boost::any const& dat)
  : m_type(data)
  , m_error()
  , m_datum(dat)
{
}

datum_exception
::datum_exception() throw()
  : pipeline_exception()
{
}

datum_exception
::~datum_exception() throw()
{
}

static char const* string_for_type(datum::type_t type);

bad_datum_cast_exception
::bad_datum_cast_exception(std::string const& typeid_, datum::type_t const& type, datum::error_t const& error, char const* reason) throw()
  : datum_exception()
  , m_typeid(typeid_)
  , m_type(type)
  , m_error(error)
  , m_reason(reason)
{
  std::ostringstream sstr;

  if (m_type == datum::error)
  {
    sstr << "Failed to cast datum of type "
            "\'" << string_for_type(m_type) << "\' (" << m_error << ") into "
         << m_typeid << ": "
         << m_reason << ".";
  }
  else
  {
    sstr << "Failed to cast datum of type "
            "\'" << string_for_type(m_type) << "\' into " << m_typeid << ": "
         << m_reason << ".";
  }

  m_what = sstr.str();
}

bad_datum_cast_exception
::~bad_datum_cast_exception() throw()
{
}

char const*
string_for_type(datum::type_t type)
{
  switch (type)
  {
#define STRING_FOR_TYPE(type) \
  case datum::type:           \
    return #type

    STRING_FOR_TYPE(data);
    STRING_FOR_TYPE(empty);
    STRING_FOR_TYPE(error);
    STRING_FOR_TYPE(invalid);
    STRING_FOR_TYPE(flush);
    STRING_FOR_TYPE(complete);

#undef STRING_FOR_TYPE

    default:
      break;
  }

  return "unknown";
}

}
<|endoftext|>


<commit_before>/*ckwg +5
 * Copyright 2011 by Kitware, Inc. All Rights Reserved. Please refer to
 * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel,
 * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065.
 */

#include "datum.h"

#include <sstream>

/**
 * \file datum.cxx
 *
 * \brief Implementation of a piece of \link vistk::datum data\endlink in the pipeline.
 */

namespace vistk
{

datum_t
datum
::empty_datum()
{
  return datum_t(new datum(false));
}

datum_t
datum
::complete_datum()
{
  return datum_t(new datum(true));
}

datum_t
datum
::error_datum(error_t const& error)
{
  return datum_t(new datum(error));
}

datum::datum_type_t
datum
::type() const
{
  return m_type;
}

datum::error_t
datum
::get_error() const
{
  return m_error;
}

datum
::datum(bool is_complete)
  : m_type(is_complete ? DATUM_EMPTY : DATUM_COMPLETE)
{
}

datum
::datum(error_t const& error)
  : m_type(DATUM_ERROR)
  , m_error(error)
{
}

datum
::datum(boost::any const& dat)
  : m_type(DATUM_DATA)
  , m_datum(dat)
{
}

bad_datum_cast_exception
::bad_datum_cast_exception(datum::datum_type_t const& type, char const* reason) throw()
  : datum_exception()
  , m_type(type)
  , m_reason(reason)
{
  std::ostringstream sstr;

  sstr << "Failed to cast key datum of type "
       << "\'" << m_type << "\': " << m_reason << ".";

  m_what = sstr.str();
}

bad_datum_cast_exception
::~bad_datum_cast_exception() throw()
{
}

char const*
bad_datum_cast_exception
::what() const throw()
{
  return m_what.c_str();
}

}
<commit_msg>Flip backwards logic around<commit_after>/*ckwg +5
 * Copyright 2011 by Kitware, Inc. All Rights Reserved. Please refer to
 * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel,
 * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065.
 */

#include "datum.h"

#include <sstream>

/**
 * \file datum.cxx
 *
 * \brief Implementation of a piece of \link vistk::datum data\endlink in the pipeline.
 */

namespace vistk
{

datum_t
datum
::empty_datum()
{
  return datum_t(new datum(false));
}

datum_t
datum
::complete_datum()
{
  return datum_t(new datum(true));
}

datum_t
datum
::error_datum(error_t const& error)
{
  return datum_t(new datum(error));
}

datum::datum_type_t
datum
::type() const
{
  return m_type;
}

datum::error_t
datum
::get_error() const
{
  return m_error;
}

datum
::datum(bool is_complete)
  : m_type(is_complete ? DATUM_COMPLETE : DATUM_EMPTY)
{
}

datum
::datum(error_t const& error)
  : m_type(DATUM_ERROR)
  , m_error(error)
{
}

datum
::datum(boost::any const& dat)
  : m_type(DATUM_DATA)
  , m_datum(dat)
{
}

bad_datum_cast_exception
::bad_datum_cast_exception(datum::datum_type_t const& type, char const* reason) throw()
  : datum_exception()
  , m_type(type)
  , m_reason(reason)
{
  std::ostringstream sstr;

  sstr << "Failed to cast key datum of type "
       << "\'" << m_type << "\': " << m_reason << ".";

  m_what = sstr.str();
}

bad_datum_cast_exception
::~bad_datum_cast_exception() throw()
{
}

char const*
bad_datum_cast_exception
::what() const throw()
{
  return m_what.c_str();
}

}
<|endoftext|>


<commit_before>#include "replayState.h"

#include <deque>
#include <boost/bind.hpp>
#include <boost/thread.hpp>

#include "libwatcher/message.h"
#include "serverConnection.h"
#include "Assert.h"
#include "database.h"
#include "watcherd.h"

using namespace util;
using namespace watcher;
using namespace watcher::event;

//< default value for number of events to prefetch from the database
const unsigned int DEFAULT_BUFFER_SIZE = 20U; /* db rows */
const unsigned int DEFAULT_STEP = 250U /* ms */;

/** Internal structure used for implementing the class.  Used to avoid
 * dependencies for the user of the class.  These would normally be private
 * members of ReplayState.
 */
struct ReplayState::impl {
    boost::weak_ptr<ServerConnection> conn;
    std::deque<MessagePtr> events;
    boost::asio::deadline_timer timer;
    Timestamp ts; // the current effective time
    Timestamp last_event; // timestamp of last event retrieved from db
    float speed; //< playback speed
    unsigned int bufsiz; //< number of database rows to prefetch
    Timestamp step;
    enum run_state { paused, running } state;
    timeval wall_time; //< used to correct for clock skew
    Timestamp delta;

    /*
     * Lock used for event queue.  This is required due to the seek() member
     * function, which can be called from a different thread.
     */
    boost::mutex lock;

    impl(ServerConnectionPtr& ptr) :
        conn(ptr), timer(ptr->io_service()), ts(0), last_event(0), bufsiz(DEFAULT_BUFFER_SIZE),
        step(DEFAULT_STEP), state(paused), delta(0)
    {
        TRACE_ENTER();
        wall_time.tv_sec = 0;
        wall_time.tv_usec = 0;
        TRACE_EXIT();
    }
};

ReplayState::ReplayState(ServerConnectionPtr ptr, Timestamp t, float playback_speed) :
    impl_(new impl(ptr))
{
    TRACE_ENTER();
    Assert<Bad_arg>(t >= 0);
    impl_->ts = t;
    impl_->last_event = t;

    speed(playback_speed);
    TRACE_EXIT();
}

Timestamp ReplayState::tell() const
{
    TRACE_ENTER();
    TRACE_EXIT_RET(impl_->ts);
    return impl_->ts;
}

ReplayState& ReplayState::pause()
{
    TRACE_ENTER();
    LOG_DEBUG("cancelling timer");
    impl_->timer.cancel();
    impl_->state = impl::paused;
    TRACE_EXIT();
    return *this;
}

ReplayState& ReplayState::seek(Timestamp t)
{
    TRACE_ENTER();

    impl::run_state oldstate;
    {
        boost::mutex::scoped_lock L(impl_->lock);
        oldstate = impl_->state;
        pause();
        impl_->events.clear();
        impl_->ts = t;
        if (t == -1)
            impl_->last_event = std::numeric_limits<Timestamp>::max();
        else
            impl_->last_event = t;
    }
    if (oldstate == impl::running)
        run();
    TRACE_EXIT();
    return *this;
}

ReplayState& ReplayState::speed(float f)
{
    TRACE_ENTER();
    Assert<Bad_arg>(f != 0);
    /* If speed changes direction, need to clear the event list.
     * Check for sign change by noting that positive*negative==negative
     */
    if (impl_->speed * f < 0) {
        impl::run_state oldstate;
        {
            boost::mutex::scoped_lock L(impl_->lock);

            oldstate = impl_->state;
            pause();

            impl_->events.clear();

            /*
             * Avoid setting .last_event when SpeedMessage is received
             * prior to the first StartMessage.
             */
            if (impl_->ts != 0 && impl_->ts != -1)
                impl_->last_event = impl_->ts;

            impl_->speed = f;
        }
        if (oldstate == impl::running)
            run();
    } else
        impl_->speed = f;
    LOG_DEBUG("ts=" << impl_->ts << " last_event=" << impl_->last_event);
    TRACE_EXIT();
    return *this;
}

ReplayState& ReplayState::buffer_size(unsigned int n)
{
    TRACE_ENTER();
    Assert<Bad_arg>(n != 0);
    impl_->bufsiz = n;
    TRACE_EXIT();
    return *this;
}

ReplayState& ReplayState::time_step(unsigned int n)
{
    TRACE_ENTER();
    Assert<Bad_arg>(n != 0);
    impl_->step = n;
    TRACE_EXIT();
    return *this;
}

/* function object for accepting events output from Database::getEvents() */
struct event_output {
    std::deque<MessagePtr>& q;
    event_output(std::deque<MessagePtr>& qq) : q(qq) {}
    void operator() (MessagePtr m) { q.push_back(m); }
};

/** Schedule an asynchronous task to replay events from the database to a GUI
 * client.  If the local cache of upcoming events is empty, prefetch a block of
 * events from the database.
 *
 * The code is written such that it will work when playing events forward or in
 * reverse.
 */
void ReplayState::run()
{
    TRACE_ENTER();
    boost::mutex::scoped_lock L(impl_->lock);

    if (impl_->events.empty()) {
        // queue is empty, pre-fetch more items from the DB

        boost::function<void(MessagePtr)> cb(event_output(impl_->events));
        LOG_DEBUG("fetching events " << (impl_->speed > 0 ? "> " : "< ") << impl_->last_event);
        get_db_handle().getEvents(cb,
                                  impl_->last_event,
                                  (impl_->speed >= 0) ? Database::forward : Database::reverse,
                                  impl_->bufsiz);

        if (!impl_->events.empty()) {
            LOG_DEBUG("got " << impl_->events.size() << " events from the db query");
            /* When starting to replay, assume that time T=0 is the time of the
             * first event in the stream.
             * T= -1 is EOF.
             * Convert to timestamp of first item in the returned events.
             *
             * When playing in reverse, the first item in the list is the last event in the database.
             */
            if (impl_->ts == 0 || impl_->ts == -1)
                impl_->ts = impl_->events.front()->timestamp;

            // save timestamp of last event retrieved to avoid duplication
            impl_->last_event = impl_->events.back()->timestamp;
        }
    }

    if (! impl_->events.empty()) {
        /*
         * Calculate for the skew introduced by the time required to process the events.  Skew is calculated
         * as the difference between the actual time taken and the expected time.  This gets
         * subtracted from the wait for the next event to catch up.
         */
        timeval tv;
        gettimeofday(&tv, 0);
        Timestamp skew = (tv.tv_sec - impl_->wall_time.tv_sec) * 1000 + (tv.tv_usec - impl_->wall_time.tv_usec) / 1000;
        skew -= impl_->delta;
        LOG_DEBUG("calculated skew of " << skew << " ms");
        memcpy(&impl_->wall_time, &tv, sizeof(tv));

        // time until next event
        impl_->delta = impl_->events.front()->timestamp - impl_->ts;

        // update our notion of the current time after the timer expires
        impl_->ts = impl_->events.front()->timestamp;

        /* Adjust for playback speed.  Note that when playing events in reverse, both speed
         * delta will be negative, which will turn delta into a positive value for the
         * async_wait() call, which is exactly what is required.  */
        impl_->delta /= (Timestamp)impl_->speed;

        /* Correct for skew */
        impl_->delta -= skew;
        if (impl_->delta < 0)
            impl_->delta = 0;

        LOG_DEBUG("Next event in " << impl_->delta << " ms");
        impl_->timer.expires_from_now(boost::posix_time::millisec(impl_->delta));
        impl_->timer.async_wait(boost::bind(&ReplayState::timer_handler, shared_from_this(), boost::asio::placeholders::error));
        impl_->state = impl::running;
    } else {
        /*
         * FIXME what should happen when the end of the event stream is reached?
         * One option would be to convert to live stream at this point.
         */
        LOG_DEBUG("reached end of database, pausing playback");
        impl_->state = impl::paused;
    }
    TRACE_EXIT();
}

/** Replay events to a GUI client when a timer expires.
 *
 * The run() member function is reponsible for prefetching events from the
 * database and storing them in the class object.  When a timer expires, run
 * through the locally stored events and send those that occurred within the
 * last time slice.  The task is then rescheduled when the next most recent
 * event needs to be transmitted.
 */
void ReplayState::timer_handler(const boost::system::error_code& ec)
{
    TRACE_ENTER();
    if (ec == boost::asio::error::operation_aborted)
        LOG_DEBUG("timer was cancelled");
    else if (impl_->state == impl::paused) {
        LOG_DEBUG("timer expired but state is paused!");
    } else {
        std::vector<MessagePtr> msgs;
        {
            boost::mutex::scoped_lock L(impl_->lock);

            while (! impl_->events.empty()) {
                MessagePtr m = impl_->events.front();
                /* Replay all events in the current time step.  Use the absolute value
                 * of the difference in order for forward and reverse replay to work
                 * properly. */
                if (abs(m->timestamp - impl_->ts) >= impl_->step)
                    break;
                msgs.push_back(m);
                impl_->events.pop_front();
            }
        }

        ServerConnectionPtr srv = impl_->conn.lock();
        if (srv) { /* connection is still alive */
            srv->sendMessage(msgs);
            run(); // reschedule this task
        }
    }
    TRACE_EXIT();
}

/* This is required to be defined, otherwise a the default dtor will cause a
 * compiler error due to use of scoped_ptr with an incomplete type.
 */
ReplayState::~ReplayState()
{
    TRACE_ENTER();
    TRACE_EXIT();
}

float ReplayState::speed() const
{
    return impl_->speed;
}
<commit_msg>do correct casting.<commit_after>#include "replayState.h"

#include <deque>
#include <boost/bind.hpp>
#include <boost/thread.hpp>

#include "libwatcher/message.h"
#include "serverConnection.h"
#include "Assert.h"
#include "database.h"
#include "watcherd.h"

using namespace util;
using namespace watcher;
using namespace watcher::event;

//< default value for number of events to prefetch from the database
const unsigned int DEFAULT_BUFFER_SIZE = 20U; /* db rows */
const unsigned int DEFAULT_STEP = 250U /* ms */;

/** Internal structure used for implementing the class.  Used to avoid
 * dependencies for the user of the class.  These would normally be private
 * members of ReplayState.
 */
struct ReplayState::impl {
    boost::weak_ptr<ServerConnection> conn;
    std::deque<MessagePtr> events;
    boost::asio::deadline_timer timer;
    Timestamp ts; // the current effective time
    Timestamp last_event; // timestamp of last event retrieved from db
    float speed; //< playback speed
    unsigned int bufsiz; //< number of database rows to prefetch
    Timestamp step;
    enum run_state { paused, running } state;
    timeval wall_time; //< used to correct for clock skew
    Timestamp delta;

    /*
     * Lock used for event queue.  This is required due to the seek() member
     * function, which can be called from a different thread.
     */
    boost::mutex lock;

    impl(ServerConnectionPtr& ptr) :
        conn(ptr), timer(ptr->io_service()), ts(0), last_event(0), bufsiz(DEFAULT_BUFFER_SIZE),
        step(DEFAULT_STEP), state(paused), delta(0)
    {
        TRACE_ENTER();
        wall_time.tv_sec = 0;
        wall_time.tv_usec = 0;
        TRACE_EXIT();
    }
};

ReplayState::ReplayState(ServerConnectionPtr ptr, Timestamp t, float playback_speed) :
    impl_(new impl(ptr))
{
    TRACE_ENTER();
    Assert<Bad_arg>(t >= 0);
    impl_->ts = t;
    impl_->last_event = t;

    speed(playback_speed);
    TRACE_EXIT();
}

Timestamp ReplayState::tell() const
{
    TRACE_ENTER();
    TRACE_EXIT_RET(impl_->ts);
    return impl_->ts;
}

ReplayState& ReplayState::pause()
{
    TRACE_ENTER();
    LOG_DEBUG("cancelling timer");
    impl_->timer.cancel();
    impl_->state = impl::paused;
    TRACE_EXIT();
    return *this;
}

ReplayState& ReplayState::seek(Timestamp t)
{
    TRACE_ENTER();

    impl::run_state oldstate;
    {
        boost::mutex::scoped_lock L(impl_->lock);
        oldstate = impl_->state;
        pause();
        impl_->events.clear();
        impl_->ts = t;
        if (t == -1)
            impl_->last_event = std::numeric_limits<Timestamp>::max();
        else
            impl_->last_event = t;
    }
    if (oldstate == impl::running)
        run();
    TRACE_EXIT();
    return *this;
}

ReplayState& ReplayState::speed(float f)
{
    TRACE_ENTER();
    Assert<Bad_arg>(f != 0);
    /* If speed changes direction, need to clear the event list.
     * Check for sign change by noting that positive*negative==negative
     */
    if (impl_->speed * f < 0) {
        impl::run_state oldstate;
        {
            boost::mutex::scoped_lock L(impl_->lock);

            oldstate = impl_->state;
            pause();

            impl_->events.clear();

            /*
             * Avoid setting .last_event when SpeedMessage is received
             * prior to the first StartMessage.
             */
            if (impl_->ts != 0 && impl_->ts != -1)
                impl_->last_event = impl_->ts;

            impl_->speed = f;
        }
        if (oldstate == impl::running)
            run();
    } else
        impl_->speed = f;
    LOG_DEBUG("ts=" << impl_->ts << " last_event=" << impl_->last_event);
    TRACE_EXIT();
    return *this;
}

ReplayState& ReplayState::buffer_size(unsigned int n)
{
    TRACE_ENTER();
    Assert<Bad_arg>(n != 0);
    impl_->bufsiz = n;
    TRACE_EXIT();
    return *this;
}

ReplayState& ReplayState::time_step(unsigned int n)
{
    TRACE_ENTER();
    Assert<Bad_arg>(n != 0);
    impl_->step = n;
    TRACE_EXIT();
    return *this;
}

/* function object for accepting events output from Database::getEvents() */
struct event_output {
    std::deque<MessagePtr>& q;
    event_output(std::deque<MessagePtr>& qq) : q(qq) {}
    void operator() (MessagePtr m) { q.push_back(m); }
};

/** Schedule an asynchronous task to replay events from the database to a GUI
 * client.  If the local cache of upcoming events is empty, prefetch a block of
 * events from the database.
 *
 * The code is written such that it will work when playing events forward or in
 * reverse.
 */
void ReplayState::run()
{
    TRACE_ENTER();
    boost::mutex::scoped_lock L(impl_->lock);

    if (impl_->events.empty()) {
        // queue is empty, pre-fetch more items from the DB

        boost::function<void(MessagePtr)> cb(event_output(impl_->events));
        LOG_DEBUG("fetching events " << (impl_->speed > 0 ? "> " : "< ") << impl_->last_event);
        get_db_handle().getEvents(cb,
                                  impl_->last_event,
                                  (impl_->speed >= 0) ? Database::forward : Database::reverse,
                                  impl_->bufsiz);

        if (!impl_->events.empty()) {
            LOG_DEBUG("got " << impl_->events.size() << " events from the db query");
            /* When starting to replay, assume that time T=0 is the time of the
             * first event in the stream.
             * T= -1 is EOF.
             * Convert to timestamp of first item in the returned events.
             *
             * When playing in reverse, the first item in the list is the last event in the database.
             */
            if (impl_->ts == 0 || impl_->ts == -1)
                impl_->ts = impl_->events.front()->timestamp;

            // save timestamp of last event retrieved to avoid duplication
            impl_->last_event = impl_->events.back()->timestamp;
        }
    }

    if (! impl_->events.empty()) {
        /*
         * Calculate for the skew introduced by the time required to process the events.  Skew is calculated
         * as the difference between the actual time taken and the expected time.  This gets
         * subtracted from the wait for the next event to catch up.
         */
        timeval tv;
        gettimeofday(&tv, 0);
        Timestamp skew = (tv.tv_sec - impl_->wall_time.tv_sec) * 1000 + (tv.tv_usec - impl_->wall_time.tv_usec) / 1000;
        skew -= impl_->delta;
        LOG_DEBUG("calculated skew of " << skew << " ms");
        memcpy(&impl_->wall_time, &tv, sizeof(tv));

        // time until next event
        impl_->delta = impl_->events.front()->timestamp - impl_->ts;

        // update our notion of the current time after the timer expires
        impl_->ts = impl_->events.front()->timestamp;

        /* Adjust for playback speed.  Note that when playing events in reverse, both speed
         * delta will be negative, which will turn delta into a positive value for the
         * async_wait() call, which is exactly what is required.  */
        impl_->delta = (Timestamp)(impl_->delta/impl_->speed);

        /* Correct for skew */
        impl_->delta -= skew;
        if (impl_->delta < 0)
            impl_->delta = 0;

        LOG_DEBUG("Next event in " << impl_->delta << " ms");
        impl_->timer.expires_from_now(boost::posix_time::millisec(impl_->delta));
        impl_->timer.async_wait(boost::bind(&ReplayState::timer_handler, shared_from_this(), boost::asio::placeholders::error));
        impl_->state = impl::running;
    } else {
        /*
         * FIXME what should happen when the end of the event stream is reached?
         * One option would be to convert to live stream at this point.
         */
        LOG_DEBUG("reached end of database, pausing playback");
        impl_->state = impl::paused;
    }
    TRACE_EXIT();
}

/** Replay events to a GUI client when a timer expires.
 *
 * The run() member function is reponsible for prefetching events from the
 * database and storing them in the class object.  When a timer expires, run
 * through the locally stored events and send those that occurred within the
 * last time slice.  The task is then rescheduled when the next most recent
 * event needs to be transmitted.
 */
void ReplayState::timer_handler(const boost::system::error_code& ec)
{
    TRACE_ENTER();
    if (ec == boost::asio::error::operation_aborted)
        LOG_DEBUG("timer was cancelled");
    else if (impl_->state == impl::paused) {
        LOG_DEBUG("timer expired but state is paused!");
    } else {
        std::vector<MessagePtr> msgs;
        {
            boost::mutex::scoped_lock L(impl_->lock);

            while (! impl_->events.empty()) {
                MessagePtr m = impl_->events.front();
                /* Replay all events in the current time step.  Use the absolute value
                 * of the difference in order for forward and reverse replay to work
                 * properly. */
                if (abs(m->timestamp - impl_->ts) >= impl_->step)
                    break;
                msgs.push_back(m);
                impl_->events.pop_front();
            }
        }

        ServerConnectionPtr srv = impl_->conn.lock();
        if (srv) { /* connection is still alive */
            srv->sendMessage(msgs);
            run(); // reschedule this task
        }
    }
    TRACE_EXIT();
}

/* This is required to be defined, otherwise a the default dtor will cause a
 * compiler error due to use of scoped_ptr with an incomplete type.
 */
ReplayState::~ReplayState()
{
    TRACE_ENTER();
    TRACE_EXIT();
}

float ReplayState::speed() const
{
    return impl_->speed;
}
<|endoftext|>