text:
/**
* The MIT License (MIT)
*
* Copyright (c) 2015 Filippo Bergamasco
*
* 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 <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/calib3d.hpp>
#include <opencv2/core/core_c.h>

#include <boost/program_options.hpp>
#include <iostream>
#include "runetag.hpp"
#include "auxrenderer.hpp"
#include "ellipsefitter.hpp"

#include "markerpose.hpp"
#include "coding.h"

#define ENABLE_TESTS

#ifdef ENABLE_TESTS
#include "test_occlusion.h"
#endif

using namespace cv;

namespace po = boost::program_options;

int main (int argc, char** argv)
{

    std::cout << " _________________________________________________________" << std::endl;
    std::cout << "|                                                         |"<<std::endl;
    std::cout << "|      RUNEtag detector                                   |" << std::endl;
    std::cout << "|                                                         |"<<std::endl;
    std::cout << " \\________________________________________________________|" << std::endl;
    std::cout << std::endl;

    /************************************************************************/
    /*         Settings & Data                                              */
    /************************************************************************/
    unsigned short min_ellipse_contour_points=10;
    unsigned short max_ellipse_contour_points=10000;
    float min_ellipse_area=100.0f;
    float max_ellipse_area=10000.0f;
    float min_roundness=0.3f;
    float max_mse = 0.3f;
    float size_compensation=-1.5f;
	bool dorefine;
	float gauss_smooth_sigma;
	int sobel_winsize;

#ifdef ENABLE_TESTS
	bool doocclusiontest;
	std::string occlusion_test_filename;
	int num_occlusion_tests;
	bool occlusion_test_interactive;
#endif

    cv::Mat input_image;
    std::string ellipseimg_filename;
    std::string tagsimg_filename;
    std::string points_filename;
    std::string pose_filename;

	bool pnpransac;
	bool pnprefine;
	unsigned int method;

    cv::runetag::MarkerDetector* pDetector=0;
    cv::Mat intrinsics;

    /************************************************************************/
    /*         Program Options setup                                        */
    /************************************************************************/
    po::options_description input_opts("Input");
    input_opts.add_options()
        ("config",po::value< std::string >(),"config filename")
        ("dir",po::value< std::string >(),"data directory")
        ("img",po::value< std::string >(),"image filename")
        ("m",po::value< std::vector<std::string> >(),"tag model file (this option can be repeated many times)")
        ("f",po::value< double >(),"focal length (both fx and fy)")
        ("fx",po::value< double >(),"focal length x")
        ("fy",po::value< double >(),"focal length y")
        ("cx",po::value< double >(),"optical center x")
        ("cy",po::value< double >(),"optical center y")
		("intrinsicsfile",po::value< std::string >(),"intrinsics matrix (opencv format)")
		("distortionfile",po::value< std::string >(),"distortion matrix (opencv format)")
#ifdef ENABLE_TESTS
		("occlusion_test",po::bool_switch(&doocclusiontest),"Perform the occlusion test")
		("occlusion_test_filename",po::value< std::string >(&occlusion_test_filename),"Occlusion test results filename")
		("occlusion_test_n",po::value< int >(&num_occlusion_tests)->default_value( 10 ),"Number of occlusion tests to perform")
		("occlusion_test_interactive",po::bool_switch(&occlusion_test_interactive),"View occlusion test working")
#endif
        ;

    po::options_description output_opts("Output");
    output_opts.add_options()
        ("ellipseimg",po::value< std::string >(),"Render detected ellipses to image file")
        ("tagsimg",po::value< std::string >(),"Render detected tags to image file")
        ("pointsfile",po::value< std::string >(),"Detected tags points filename")
        ("posefile",po::value< std::string >(),"Detected tags pose filename")
        ;

    po::options_description ellipse_opts("Ellipse detector");
    ellipse_opts.add_options()
        ("minarea",po::value< float >(),"Minimum ellipse area [100]")
        ("maxarea",po::value< float >(),"Maximum ellipse area [10000]")
        ("minroundness",po::value< float >(),"Minimum ellipse roundness [0.3]")
        ("maxmse",po::value< float >(),"Maxmimum ellipse contour error [0.3]")
		("refine",po::bool_switch(&dorefine),"Refine ellipses")
		("pnpmethod",po::value<unsigned int>(&method)->default_value(0),"PnP Method: 0=Iterative, 1=ePnP")
		("pnpransac",po::bool_switch(&pnpransac),"Use robust ransac estimation for PnP")
		("pnprefine",po::bool_switch(&pnprefine),"Refine ellipse centers with PnP")
		("refine_gauss_smooth_sigma",po::value< float >(&gauss_smooth_sigma)->default_value( 3.0 ),"Gaussian blur sigma to be applied before sobel")
		("sobel_winsize",po::value< int >(&sobel_winsize)->default_value( 3 ),"Sobel window size")
        ;

    bool load_config_file = false;

    try {

        std::string config_filename;
        {
            // Check if "config" option is set in command line arguments
            po::options_description cmdline_options;
            cmdline_options.add(input_opts).add(output_opts).add(ellipse_opts);
            po::variables_map varmap;
            po::store( po::parse_command_line(argc, argv, cmdline_options), varmap );
            po::notify(varmap);
            if( varmap.count("config") )
            {
                config_filename = varmap["config"].as< std::string >();
                load_config_file = true;
            }
        }

        po::options_description cmdline_options;
        cmdline_options.add(input_opts).add(output_opts).add(ellipse_opts);
        po::variables_map varmap;

        if( load_config_file )
        {
            std::ifstream ifs( config_filename.c_str() );
            if( ifs.fail() )
            {
                std::cout << "Unable to open " << config_filename << std::endl;
            } else
            {
                po::store( po::parse_config_file(ifs, cmdline_options ), varmap );
                ifs.close();
            }
        }
        po::store( po::parse_command_line(argc, argv, cmdline_options), varmap );
        po::notify(varmap);

        if (argc == 1 || varmap.count("help"))
        {
            std::cout << cmdline_options << "\n";
            return 1;
        }

        /************************************************************************/
        /*  Process input arguments                                             */
        /************************************************************************/

        std::string workdir;
        if( varmap.count("dir") )
        {
            workdir = varmap["dir"].as<std::string>();
            if( workdir.length()>0 && workdir[workdir.length()-1]!='\\' && workdir[workdir.length()-1]!='/' )
            {
                workdir = workdir + std::string("\\");
            }
            std::cout << "+ Workdir set to: " << std::endl << workdir << std::endl << std::endl;
        }

        std::string img_filename;
        if( varmap.count("img") )
        {
            img_filename = varmap["img"].as<std::string>();
            if( workdir.length()>0 )
            {
                img_filename = workdir + img_filename;
            }
            std::cout << "+ Image filename: " << std::endl << img_filename << std::endl << std::endl;
        }
        else
        {
            std::cout << "Error: you must specify image filename" << std::endl;
            return -1;
        }

        // Read input image
        input_image = cv::imread( img_filename );
        if( input_image.rows==0 || input_image.cols==0 )
        {
            std::cout << "Error: unable to open input image" << std::endl;
            return -1;
        }

        // Input calibration
        intrinsics = cv::Mat::eye(3,3,CV_64FC1);

		// if( varmap.count("intrinsicsfile") )
    //     {
		// 	std::string intrinsics_filename = varmap["intrinsicsfile"].as<std::string>();
		// 	try {
		// 		intrinsics = cvarrToMat( cvLoad( intrinsics_filename.c_str() ), true );
		// 	} catch( cv::Exception e ) {
		// 		std::cout << "Unable to load " << intrinsics_filename << ", aborting" << std::endl;
		// 		return -1;
		// 	}
		// }
		// else
		// {
			if( varmap.count("f") )
			{
				intrinsics.at<double>(0,0) = intrinsics.at<double>(1,1) = varmap["f"].as<double>();
			}
			if( varmap.count("fx") )
			{
				intrinsics.at<double>(0,0) = varmap["fx"].as<double>();
			}
			if( varmap.count("fy") )
			{
				intrinsics.at<double>(1,1) = varmap["fy"].as<double>();
			}
			if( varmap.count("cx") )
			{
				intrinsics.at<double>(0,2) = varmap["cx"].as<double>();
			} else
			{
				std::cout << "- cx not set, assuming image.cols/2" << std::endl;
				intrinsics.at<double>(0,2) = input_image.cols/2.0;
			}
			if( varmap.count("cy") )
			{
				intrinsics.at<double>(1,2) = varmap["cy"].as<double>();
			} else
			{
				std::cout << "- cy not set, assuming image.rows/2" << std::endl;
				intrinsics.at<double>(1,2) = input_image.rows/2.0;
			}
		//}

        std::cout << "+ Camera intrinsics: " << std::endl;
        std::cout << "     fx: " << intrinsics.at<double>(0,0) << std::endl;
        std::cout << "     fy: " << intrinsics.at<double>(1,1) << std::endl;
        std::cout << "     cx: " << intrinsics.at<double>(0,2) << std::endl;
        std::cout << "     cy: " << intrinsics.at<double>(1,2) << std::endl << std::endl;

		cv::Mat distortion;
		// if( varmap.count("distortionfile") )
    //     {
		// 	std::string distortion_filename = varmap["distortionfile"].as<std::string>();
		// 	try {
		// 		distortion = cvarrToMat( cvLoad( distortion_filename.c_str() ), true );
    //
		// 		std::cout << "+ Distortion coefficients: " << std::endl;
		// 		std::cout << "    " << distortion << std::endl;
		// 	} catch( cv::Exception e ) {
		// 		std::cout << "Unable to load " << distortion_filename << ", skipping" << std::endl;
		// 	}
		// }

		if( distortion.rows!=0 && distortion.cols!=0 )
		{
			std::cout << "+ Undistorting input image" << std::endl;
			cv::Mat input_image_undistorted;
			cv::undistort(input_image, input_image_undistorted, intrinsics, distortion );
			input_image = input_image_undistorted;
		}

        pDetector = new cv::runetag::MarkerDetector( intrinsics );

        if( varmap.count("m") )
        {
            std::vector< std::string > models = varmap["m"].as< std::vector<std::string> >();

            std::cout << "+ Loading models: " << std::endl;
            for( std::vector< std::string >::const_iterator it=models.begin(); it!=models.end(); ++it )
            {
                std::string model_name = workdir + *it;

                std::cout << "  " << model_name << " ";

                try
                {
                    std::cout << " " << pDetector->addModelsFromFile( model_name ) <<  " loaded" << std::endl;
                } catch( cv::runetag::DigitalMarkerModel::MarkerModelLoadException& ex )
                {
                    std::cout << std::endl << ex.what() << std::endl;
                    return -1;
                }
            }
            std::cout << std::endl;

        } else
        {
            std::cout << "Error: you must specify at least one model file" << std::endl;
            return -1;
        }

        if( varmap.count("ellipseimg") )
        {
            ellipseimg_filename = varmap["ellipseimg"].as<std::string>();
            if( workdir.length()>0 )
            {
                ellipseimg_filename = workdir + ellipseimg_filename;
            }
        }

        if( varmap.count("tagsimg") )
        {
            tagsimg_filename = varmap["tagsimg"].as<std::string>();
            if( workdir.length()>0 )
            {
                tagsimg_filename = workdir + tagsimg_filename;
            }
        }

        if( varmap.count("pointsfile") )
        {
            points_filename = varmap["pointsfile"].as<std::string>();
            if( workdir.length()>0 )
            {
                points_filename = workdir + points_filename;
            }
        }

        if( varmap.count("posefile") )
        {
           pose_filename = varmap["posefile"].as<std::string>();
            if( workdir.length()>0 )
            {
                pose_filename = workdir + pose_filename;
            }
        }

        if( varmap.count("minarea") )
        {
            min_ellipse_area = varmap["minarea"].as<float>();
        }

        if( varmap.count("maxarea") )
        {
            max_ellipse_area = varmap["maxarea"].as<float>();
        }

        if( varmap.count("minroundness") )
        {
            min_roundness = varmap["minroundness"].as<float>();
        }

        if( varmap.count("maxmse") )
        {
            max_mse = varmap["maxmse"].as<float>();
        }

    } catch( boost::program_options::error&  e )
    {
        std::cout << "Error: " << e.what() << std::endl;
        return -1;
    }

    // Detect ellipses
    std::vector< RotatedRect > foundEllipses;
    runetag::EllipseDetector ellipseDetector( min_ellipse_contour_points,
        max_ellipse_contour_points,
        min_ellipse_area,
        max_ellipse_area,
        min_roundness,
        max_mse,
        size_compensation);

    std::cout << "> Detecting ellipses" << std::endl;
    ellipseDetector.detectEllipses( input_image, foundEllipses );
    std::cout << "  " << foundEllipses.size() << " found." << std::endl << std::endl;

    if( ellipseimg_filename.length()>0 )
    {
        // Debug ellipses
        cv::Mat dbgout = input_image.clone();

        for( size_t i=0; i<foundEllipses.size(); ++i )
        {
            cv::RotatedRect rr = foundEllipses[i];
            rr.angle=-rr.angle;
            cv::ellipse( dbgout, rr, CV_RGB(255,0,0),1, LINE_AA );
        }

        std::cout << "> Rendering ellipses" << std::endl;
        std::cout << "   -> " << ellipseimg_filename << std::endl << std::endl;
        cv::imwrite( ellipseimg_filename, dbgout );
    }

    std::cout << "> Detecting RUNE tags" << std::endl;
    std::vector< cv::runetag::MarkerDetected > tags_found;
    pDetector->dbgimage = input_image.clone();
    pDetector->detectMarkers( foundEllipses, tags_found);
    std::cout << "  " << tags_found.size() << " found." << std::endl << std::endl;

	if( dorefine )
	{
		std::cout << "> Refining ellipses" << std::endl;

		if( sobel_winsize%2==0 )
		{
			sobel_winsize++;
			std::cout << " Sobel winsize changed to " << sobel_winsize << " (must be odd)" << std::endl;
		}

		// Sobel
		cv::Mat input_image_gray;
		cv::cvtColor( input_image, input_image_gray, COLOR_RGB2GRAY );

		cv::Mat input_image_smooth;
		if( gauss_smooth_sigma > 0.0f )
		{
			cv::GaussianBlur(input_image_gray,input_image_smooth,cv::Size(),gauss_smooth_sigma,gauss_smooth_sigma );
		}
		else
		{
			input_image_smooth=input_image_gray.clone();
		}
		cv::Mat gradient_x( input_image_smooth.rows, input_image_smooth.cols, CV_16S);
		cv::Mat gradient_y( input_image_smooth.rows, input_image_smooth.cols, CV_16S);
		cv::Sobel( input_image_smooth, gradient_x, CV_16S, 1, 0, sobel_winsize );
		cv::Sobel( input_image_smooth, gradient_y, CV_16S, 0, 1, sobel_winsize );

		/*cv::Mat aux;
		cv::resize( gradient_x, aux, cv::Size(),0.4,0.4 );
		cv::imshow( "gradent x", aux );
		cv::imshow( "gradent y", gradient_y );
		cv::waitKey(0);*/

		cv::Mat dbgimg;
		cv::cvtColor( input_image_smooth,dbgimg,COLOR_GRAY2RGB );

		size_t num_refined = 0;
		for( size_t i=0; i<tags_found.size(); ++i )
		{
			for( cv::runetag::MarkerDetected::SlotIterator it = tags_found[i].begin(); it!=tags_found[i].end(); ++it )
			{
				if( it->getPayload() )
				{
					//cv::runetag::AuxRenderer::drawEllipsePoint( dbgimg, *(it->getPayload()), intrinsics, CV_RGB(255,0,0) );
					if( it->getPayload()->refine( gradient_x, gradient_y, intrinsics, dbgimg ) ) {
						num_refined++;
						//cv::runetag::AuxRenderer::drawEllipsePoint( dbgimg, *(it->getPayload()), intrinsics, CV_RGB(0,255,0) );
					}
				}
			}
		}

		/*
		cv::imshow("refined",dbgimg);
		cv::waitKey(0);*/
		std::cout << "  " << num_refined << " refined. " << std::endl << std::endl;
	}

#ifdef ENABLE_TESTS
	if( tags_found.size() > 0 && doocclusiontest)
	{
		const cv::runetag::MarkerDetected& tag = tags_found[0];
		std::cout << "> Running occlusion test on the first Tag found (IDX " << tag.associatedModel()->getIDX() << ") " << std::endl;
		std::cout << "    # tests to perform: " << num_occlusion_tests << std::endl;
		std::cout << "    Interactive: " << (occlusion_test_interactive?"yes":"no") << std::endl;
		std::cout << "    result file: " << occlusion_test_filename << std::endl;

		RUNEtagdetect::test::occlusions::test_occlusions( tag, num_occlusion_tests, occlusion_test_interactive, occlusion_test_filename, input_image );

		std::cout << std::endl << "Test completed!" << std::endl;
	}
#endif

    std::cout << "> Estimating tags poses" << std::endl;
    cv::Mat distortion = cv::Mat::zeros(1,5,CV_32F);
    std::map< int, cv::runetag::Pose > poses;

    for( size_t i=0; i<tags_found.size(); ++i )
    {
        bool poseok;

        std::cout << "  Tag IDX:" << tags_found[i].associatedModel()->getIDX();

		unsigned int flags=0;
		if(pnpransac)
			flags |= cv::runetag::FLAG_REPROJ_ERROR;
		if(pnprefine)
			flags |= cv::runetag::FLAG_REFINE;

		cv::runetag::Pose RT = cv::runetag::findPose( tags_found[i], intrinsics, distortion, &poseok, method==0? SOLVEPNP_ITERATIVE : SOLVEPNP_EPNP, flags);

        if( poseok )
        {
            poses[i]=RT;
            std::cout << " OK! R(rodriguez):  [" << RT.R.at<double>(0,0) << " ; " << RT.R.at<double>(1,0) << " ; " << RT.R.at<double>(2,0) << "]" << std::endl;
            std::cout << "                     T:  [" << RT.t.at<double>(0,0) << " ; " << RT.t.at<double>(1,0) << " ; " << RT.t.at<double>(2,0) << "]" << std::endl;
        } else
        {
            std::cout << " Error." << std::endl;
        }

    }
    std::cout << std::endl;

    if( tagsimg_filename.length()>0 )
    {
        cv::Mat dbgout = input_image.clone();
        std::cout << "> Rendering tags" << std::endl;
        std::cout << "   -> " << tagsimg_filename << std::endl << std::endl;

        for( size_t i=0; i<tags_found.size(); ++i )
        {
            if( poses.find(i)!=poses.end())
            {
                cv::runetag::AuxRenderer::drawDetectedMarker3DCylinder2(dbgout,tags_found[i],poses[i],intrinsics,distortion);
            }
        }

        cv::imwrite( tagsimg_filename, dbgout );
    }

    if( points_filename.length()>0 )
    {
        std::cout << "> Saving tag points data" << std::endl;
        std::cout << "   -> " << points_filename << std::endl << std::endl;

        std::ofstream ofs( points_filename.c_str() );
        ofs.precision(15);
        ofs << std::scientific;
        if( !ofs.fail() )
        {
            for( size_t i=0; i<tags_found.size(); ++i )
            {
                for( size_t slot=0; slot<tags_found[i].getNumSlots(); ++slot )
                {
                    if( !tags_found[i].getSlot(slot).discarded() && tags_found[i].getSlot(slot).value() )
                    {
                        cv::Point2d point3d = cv::runetag::ellipseCenter( tags_found[i].associatedModel()->modelEllipseAtSlot(slot) );
                        cv::Point2d point2d = tags_found[i].getSlot(slot).getPayload()->getCenter() + cv::Point2d(intrinsics.at<double>(0,2),intrinsics.at<double>(1,2));

                        ofs << tags_found[i].associatedModel()->getIDX() << "\t" << slot << "\t" << point3d.x << "\t" << point3d.y << "\t" << "0" << "\t" << point2d.x << "\t" << point2d.y << std::endl;
                    }
                }
            }
            ofs.flush();
            ofs.close();

        } else
        {
            std::cout << "Error: Unable to open file" << std::endl;

        }
    }

    if( pose_filename.length()>0 )
    {
        std::cout << "> Saving tag pose data" << std::endl;
        std::cout << "   -> " << pose_filename << std::endl << std::endl;

        std::ofstream ofs( pose_filename.c_str() );

        ofs.precision(15);
        ofs << std::scientific;
        if( !ofs.fail() )
        {
            for( std::map< int, cv::runetag::Pose >::const_iterator it = poses.begin(); it!=poses.end(); ++it )
            {
                const cv::runetag::Pose& pose = it->second;
                const cv::runetag::MarkerDetected& marker = tags_found[it->first];

                cv::Mat rrod;
                cv::Rodrigues( pose.R, rrod );
                double angle_deg = acos( rrod.at<double>(2,2) )*180/3.14;

                ofs << marker.associatedModel()->getIDX() << "\t" << angle_deg << "\t";

                for( int ii=0; ii<3; ++ii )
                    for( int jj=0; jj<3; ++jj )
                        ofs << rrod.at<double>(ii,jj) << "\t";

                ofs << pose.t.at<double>(0,0) << "\t" << pose.t.at<double>(1,0) << "\t" << pose.t.at<double>(2,0) << std::endl;
            }

            ofs.flush();
            ofs.close();

        } else
        {
            std::cout << "Error: Unable to open file" << std::endl;

        }
    }

}

max_stars_repo_name: vshesh/RUNEtag

max_issues_repo_name: vshesh/RUNEtag

max_forks_repo_name: vshesh/RUNEtag


text:
/**********************************************************************
*  Copyright (c) 2008-2016, Alliance for Sustainable Energy.
*  All rights reserved.
*
*  This library is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public
*  License as published by the Free Software Foundation; either
*  version 2.1 of the License, or (at your option) any later version.
*
*  This library is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*  Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public
*  License along with this library; if not, write to the Free Software
*  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**********************************************************************/

#include <gtest/gtest.h>
#include "EnergyPlusFixture.hpp"

#include "../ErrorFile.hpp"
#include "../ForwardTranslator.hpp"
#include "../ReverseTranslator.hpp"

// Objects of interest
#include "../../model/GeneratorMicroTurbine.hpp"
#include "../../model/GeneratorMicroTurbine_Impl.hpp"
#include "../../model/GeneratorMicroTurbineHeatRecovery.hpp"
#include "../../model/GeneratorMicroTurbineHeatRecovery_Impl.hpp"

// Needed resources
#include "../../model/PlantLoop.hpp"
#include "../../model/PlantLoop_Impl.hpp"
#include "../../model/Node.hpp"
#include "../../model/Node_Impl.hpp"
#include "../../model/Curve.hpp"
#include "../../model/Curve_Impl.hpp"
#include "../../model/CurveBiquadratic.hpp"
#include "../../model/CurveBiquadratic_Impl.hpp"
#include "../../model/CurveCubic.hpp"
#include "../../model/CurveCubic_Impl.hpp"
#include "../../model/CurveQuadratic.hpp"
#include "../../model/CurveQuadratic_Impl.hpp"
#include "../../model/StraightComponent.hpp"
#include "../../model/StraightComponent_Impl.hpp"
#include "../../model/Schedule.hpp"
#include "../../model/Schedule_Impl.hpp"
// For testing PlantEquipOperation
#include "../../model/WaterHeaterMixed.hpp"
#include "../../model/WaterHeaterMixed_Impl.hpp"
#include "../../model/PlantEquipmentOperationHeatingLoad.hpp"
#include "../../model/PlantEquipmentOperationHeatingLoad_Impl.hpp"
#include "../../model/ElectricLoadCenterDistribution.hpp"
#include "../../model/ElectricLoadCenterDistribution_Impl.hpp"

// IDF FieldEnums
#include <utilities/idd/Generator_MicroTurbine_FieldEnums.hxx>
// #include <utilities/idd/OS_Generator_MicroTurbine_HeatRecovery_FieldEnums.hxx>
#include <utilities/idd/PlantEquipmentList_FieldEnums.hxx>
#include <utilities/idd/IddEnums.hxx>
#include <utilities/idd/IddFactory.hxx>

// Misc
#include "../../model/Version.hpp"
#include "../../model/Version_Impl.hpp"

#include "../../utilities/core/Optional.hpp"
#include "../../utilities/core/Checksum.hpp"
#include "../../utilities/core/UUID.hpp"
#include "../../utilities/sql/SqlFile.hpp"
#include "../../utilities/idf/IdfFile.hpp"
#include "../../utilities/idf/IdfObject.hpp"

#include "../../utilities/idf/IdfExtensibleGroup.hpp"

#include <boost/algorithm/string/predicate.hpp>

#include <QThread>

#include <resources.hxx>

#include <sstream>

#include <vector>

// Debug
#include "../../utilities/core/Logger.hpp"

using namespace openstudio::energyplus;
using namespace openstudio::model;
using namespace openstudio;

/**
 * Tests whether the ForwarTranslator will handle the name of the GeneratorMicroTurbine correctly in the PlantEquipmentOperationHeatingLoad
 **/
TEST_F(EnergyPlusFixture,ForwardTranslatorGeneratorMicroTurbine_ELCD_PlantLoop)
{

  // TODO: Temporarily output the Log in the console with the Trace (-3) level
  // for debug
  // openstudio::Logger::instance().standardOutLogger().enable();
  // openstudio::Logger::instance().standardOutLogger().setLogLevel(Trace);

  // Create a model, a mchp, a mchpHR, a plantLoop and an electricalLoadCenter
  Model model;

  GeneratorMicroTurbine mchp = GeneratorMicroTurbine(model);
  GeneratorMicroTurbineHeatRecovery mchpHR = GeneratorMicroTurbineHeatRecovery(model, mchp);
  ASSERT_EQ(mchpHR, mchp.generatorMicroTurbineHeatRecovery().get());

  PlantLoop plantLoop(model);
  // Add a supply branch for the mchpHR
  ASSERT_TRUE(plantLoop.addSupplyBranchForComponent(mchpHR));

  // Create a WaterHeater:Mixed
  WaterHeaterMixed waterHeater(model);
  // Add it on the same branch as the chpHR, right after it
  Node mchpHROutletNode = mchpHR.outletModelObject()->cast<Node>();
  ASSERT_TRUE(waterHeater.addToNode(mchpHROutletNode));

  // Create a plantEquipmentOperationHeatingLoad
  PlantEquipmentOperationHeatingLoad operation(model);
  operation.setName(plantLoop.name().get() + " PlantEquipmentOperationHeatingLoad");
  ASSERT_TRUE(plantLoop.setPlantEquipmentOperationHeatingLoad(operation));
  ASSERT_TRUE(operation.addEquipment(mchpHR));
  ASSERT_TRUE(operation.addEquipment(waterHeater));

  // Create an ELCD
  ElectricLoadCenterDistribution elcd = ElectricLoadCenterDistribution(model);
  elcd.setName("Capstone C65 ELCD");
  elcd.setElectricalBussType("AlternatingCurrent");
  elcd.addGenerator(mchp);

  // Forward Translates
  ForwardTranslator forwardTranslator;
  Workspace workspace = forwardTranslator.translateModel(model);

  EXPECT_EQ(0u, forwardTranslator.errors().size());
  ASSERT_EQ(1u, workspace.getObjectsByType(IddObjectType::WaterHeater_Mixed).size());
  ASSERT_EQ(1u, workspace.getObjectsByType(IddObjectType::ElectricLoadCenter_Distribution).size());
  // The MicroTurbine should have been forward translated since there is an ELCD

  WorkspaceObjectVector microTurbineObjects(workspace.getObjectsByType(IddObjectType::Generator_MicroTurbine));
  EXPECT_EQ(1u, microTurbineObjects.size());
  // Check that the HR nodes have been set
  WorkspaceObject idf_mchp(microTurbineObjects[0]);
  EXPECT_EQ(mchpHR.inletModelObject()->name().get(), idf_mchp.getString(Generator_MicroTurbineFields::HeatRecoveryWaterInletNodeName).get());
  EXPECT_EQ(mchpHR.outletModelObject()->name().get(), idf_mchp.getString(Generator_MicroTurbineFields::HeatRecoveryWaterOutletNodeName).get());

  OptionalWorkspaceObject idf_operation(workspace.getObjectByTypeAndName(IddObjectType::PlantEquipmentOperation_HeatingLoad,*(operation.name())));
  ASSERT_TRUE(idf_operation);
  // Get the extensible
  ASSERT_EQ(1u, idf_operation->numExtensibleGroups());
  // IdfExtensibleGroup eg = idf_operation.getExtensibleGroup(0);
   // idf_operation.targets[0]
  ASSERT_EQ(1u, idf_operation->targets().size());
  WorkspaceObject plantEquipmentList(idf_operation->targets()[0]);
  ASSERT_EQ(2u, plantEquipmentList.extensibleGroups().size());

  IdfExtensibleGroup eg(plantEquipmentList.extensibleGroups()[0]);
  ASSERT_EQ("Generator:MicroTurbine", eg.getString(PlantEquipmentListExtensibleFields::EquipmentObjectType).get());
  // This fails
  EXPECT_EQ(mchp.name().get(), eg.getString(PlantEquipmentListExtensibleFields::EquipmentName).get());

  IdfExtensibleGroup eg2(plantEquipmentList.extensibleGroups()[1]);
  ASSERT_EQ("WaterHeater:Mixed", eg2.getString(PlantEquipmentListExtensibleFields::EquipmentObjectType).get());
  EXPECT_EQ(waterHeater.name().get(), eg2.getString(PlantEquipmentListExtensibleFields::EquipmentName).get());

  model.save(toPath("./ForwardTranslatorGeneratorMicroTurbine_ELCD_PlantLoop.osm"), true);
  workspace.save(toPath("./ForwardTranslatorGeneratorMicroTurbine_ELCD_PlantLoop.idf"), true);

}

ext: cpp

max_stars_repo_name: hongyuanjia/OpenStudio

max_issues_repo_name: hongyuanjia/OpenStudio

max_forks_repo_name: hongyuanjia/OpenStudio


text:
#include <iostream>
#include <boost/algorithm/string/replace.hpp>

#ifdef _MSC_VER
    #pragma warning(disable : 4505) // disable "unreferenced local function has been removed" warning
#endif

#include <comm_test_common_apply.h>
#include <comm_test_common_reflection.h>
#include <comm_test_common_comm.h>

#include "transport_list.h"
#include <bond/comm/layers.h>
#include <bond/comm/timeout.h>
#include <bond/comm/thread_pool.h>

#include <boost/mpl/for_each.hpp>

// TODO: move unit_test_framework.h to cpp/test/inc
#include "../core/unit_test_framework.h"
#include "bonded_cast.h"
#include "test_utils_comm.h"
#include <boost/chrono.hpp>
#include <string.h>
#include <atomic>

using namespace unittest::comm;

const static int InnerInnerErrorCode = 0x00009012;
const static int InnerErrorCode = 0x00001234;
const static int OuterErrorCode = 0x00005678;
const static std::string InnerInnerErrorMessage = "Inner Inner error";
const static std::string InnerErrorMessage = "Inner error";
const static std::string OuterErrorMessage = "Outer error";

template <typename T>
class ErrorTransportTests
{
    typedef T Transport;

    class First_ServiceImpl
        : public First
    {
        //
        // implement First_Service
        //
        void Method1(const bond::comm::payload<Params>& input,
                     const std::function<void (const bond::comm::message<Result>&)>& callback)
        override
        {
            Params p;
            input.value().Deserialize(p);

            switch (p.op)
            {
                //
                // Throw exception.
                //
                case ThrowStdException:
                    throw std::runtime_error("ex p.op == ThrowStdException");

                case ReturnUserError:
                {
                    bond::comm::Error outerError, innerError, innerInnerError;
                    innerInnerError.error_code = InnerInnerErrorCode;
                    innerInnerError.message = InnerInnerErrorMessage;
                    innerError.error_code = InnerErrorCode;
                    innerError.message = InnerErrorMessage;
                    innerError.inner_error.set(bond::bonded<bond::comm::Error>(innerInnerError));
                    outerError.error_code = OuterErrorCode;
                    outerError.message = OuterErrorMessage;
                    outerError.inner_error.set(bond::bonded<bond::comm::Error>(innerError));
                    callback(bond::comm::error(outerError));
                    return;
                }

                //
                // Forget callback (would raise an exception).
                //
                case ForgetCallback: return;

                default: UT_AssertIsTrue(false);
            }
        }

    public:

        First_ServiceImpl()
        {}
    };

    class Second_ServiceImpl
        : public Second
    {
        //
        // implement Second_Service
        //
        void Method2(const bond::comm::payload<Params>& input,
                     const std::function<void (const bond::comm::message<Result>&)>& callback)
        override
        {
            Params p;
            input.value().Deserialize(p);

            Result r;
            r.z = 2;
            callback(boost::cref(r));
        }
    };

    static
    void Server1Service1UserError1()
    {
        //
        // user error sent by serice implementation
        // connect First::Proxy to First_Service
        //

        //
        // Create service.
        First_ServiceImpl firstService;

        bond::comm::SocketAddress address("127.0.0.1", TEST_PORT_1);

        //
        // Start transport.
        //
        Transport transport;

        // Publish First_Service twice.
        bond::comm::Server server = transport.Bind(address, boost::ref(firstService));
        auto client = transport.Connect(address);

        First::Proxy proxy(client);

        LOOP("Server1Service1UserError1")
        {
            Params params;
            Result result;

            params.op = ReturnUserError;

            WaitForResponse<Result> syncMessage;
            proxy.Method1(params, syncMessage);

            bond::comm::message<Result> message = syncMessage.GetResult();

            UT_AssertIsTrue(message.is_error());

            const bond::comm::Error &outerError = message.err();
            UT_AssertAreEqual(OuterErrorCode, outerError.error_code);
            UT_AssertAreEqual(OuterErrorMessage, outerError.message);
            UT_AssertIsTrue(outerError.inner_error.hasvalue());

            const bond::comm::Error &innerError = outerError.inner_error.value().Deserialize();
            UT_AssertAreEqual(InnerErrorCode, innerError.error_code);
            UT_AssertAreEqual(InnerErrorMessage, innerError.message);
            UT_AssertIsTrue(innerError.inner_error.hasvalue());

            const bond::comm::Error &innerInnerError = innerError.inner_error.value().Deserialize();
            UT_AssertAreEqual(InnerInnerErrorCode, innerInnerError.error_code);
            UT_AssertAreEqual(InnerInnerErrorMessage, innerInnerError.message);
        }
    }

    static
    void Server1Service1Exception1()
    {
        //
        // exception raised by serice implementation
        // connect First::Proxy to First_Service
        //

        //
        // Create service.
        First_ServiceImpl firstService;

        bond::comm::SocketAddress address("127.0.0.1", TEST_PORT_1);

        //
        // Start transport.
        //
        Transport transport;

        // Publish First_Service twice.
        bond::comm::Server server = transport.Bind(address, boost::ref(firstService));
        auto client = transport.Connect(address);

        First::Proxy proxy(client);

        LOOP("Server1Service1Exception1")
        {
            Params params;
            Result result;

            params.op = ThrowStdException;

            WaitForResponse<Result> syncMessage;
            proxy.Method1(params, syncMessage);

            bond::comm::message<Result> message = syncMessage.GetResult();

            UT_AssertIsTrue(message.is_error());
            const bond::comm::Error &outerError = message.err();
            UT_AssertAreEqual(bond::comm::ErrorCode::INTERNAL_SERVER_ERROR, outerError.error_code);
            UT_AssertAreEqual("", outerError.message);
        }
    }

    static
    void Server1Service1Exception2()
    {
        //
        // exception raised by calltable because of unknown service
        // connect First::Proxy to Second_Service
        //

        //
        // Create service.
        Second_ServiceImpl secondService;

        bond::comm::SocketAddress address("127.0.0.1", TEST_PORT_1);

        //
        // Start transport.
        //
        Transport transport;

        // Publish First_Service twice.
        bond::comm::Server server = transport.Bind(address, boost::ref(secondService));
        auto client = transport.Connect(address);

        First::Proxy proxy(client);

        LOOP("Server1Service1Exception2")
        {
            Params params;
            Result result;

            params.op = ThrowStdException;

            WaitForResponse<Result> syncMessage;
            proxy.Method1(params, syncMessage);

            UT_AssertThrows((syncMessage.Deserialize(result)), bond::Exception);
        }
    }

    static
    void Server1Service1Exception3()
    {
        //
        // exception raised by forgotten callback
        //

        // Create service.
        //
        First_ServiceImpl service;

        // Start transport.
        //
        Transport transport;

        // Publish First_Service.
        //
        bond::comm::SocketAddress address("127.0.0.1", TEST_PORT_1);
        bond::comm::Server server = transport.Bind(address, boost::ref(service));

        First::Proxy proxy(transport.Connect(address));

        LOOP("Server1Service1Exception3")
        {
            Params params;
            Result result;

            params.op = ForgetCallback;

            WaitForResponse<Result> syncMessage;
            proxy.Method1(params, syncMessage);

            UT_AssertThrows((syncMessage.Deserialize(result)), bond::Exception);
        }
    }

public:

    static
    void Initialize()
    {
        std::string name = typeid(Transport).name();

        // Shorten the name.
        boost::algorithm::replace_all(name, "struct ", "");
        boost::algorithm::replace_all(name, "class ", "");
        boost::algorithm::replace_all(name, "bond::transport::", "");
        boost::algorithm::replace_all(name, "bond::", "");

        UnitTestSuite suite(name.c_str());

        suite.AddTestCase(Server1Service1UserError1, "Server1Service1UserError1");

        suite.AddTestCase(Server1Service1Exception1, "Server1Service1Exception1");

        suite.AddTestCase(Server1Service1Exception2, "Server1Service1Exception2");

        suite.AddTestCase(Server1Service1Exception3, "Server1Service1Exception3");

    }
};

bool init_unit_test()
{
    InitializeTests<ErrorTransportTests, bond::comm::FastWireProtocol>();
    return true;
}

max_stars_repo_name: grisharav/bond

max_issues_repo_name: grisharav/bond

max_forks_repo_name: grisharav/bond


text:
/*
 * PDFedit - free program for PDF document manipulation.
 * Copyright (C) 2006-2009  PDFedit team: Michal Hocko,
 *                                        Jozef Misutka,
 *                                        Martin Petricek
 *                   Former team members: Miroslav Jahoda
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (in doc/LICENSE.GPL); if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA  02111-1307  USA
 *
 * Project is hosted on http://sourceforge.net/projects/pdfedit
 */

#include <boost/program_options.hpp>
#include <boost/shared_ptr.hpp>
#include <kernel/pdfedit-core-dev.h>
#include <kernel/cpdf.h>
#include <string>

using namespace pdfobjects;
using namespace std;
using namespace boost;
namespace po = program_options;

bool decodeStream = false;
typedef std::vector<pdfobjects::IndiRef> RefContainer;

void getStringRepresentation(boost::shared_ptr<IProperty> &prop, std::string &str)
{
	if(!decodeStream || !isStream(prop))
	{
		prop->getStringRepresentation(str);
		return;
	}

	// we want to have decoded stream
	boost::shared_ptr<CStream> streamProp = IProperty::getSmartCObjectPtr<CStream>(prop);
	streamProp->getDecodedStringRepresentation(str);
}

void print_objects(const char *fname, RefContainer &refs)
{
	boost::shared_ptr<CPdf> pdf = pdfobjects::CPdf::getInstance(fname, CPdf::ReadOnly);

	std::cout << "Document: \"" << fname << "\"" << std::endl;
	RefContainer::const_iterator i;
	for (i=refs.begin(); i!=refs.end(); ++i)
	{
		IndiRef ref = *i;
		boost::shared_ptr<IProperty> prop = pdf->getIndirectProperty(ref);
		std::string str;
		getStringRepresentation(prop, str);
		std::cout << "[" << ref << "]:" << std::endl << str << std::endl;
	}
}
int main(int argc, char ** argv)
{
	if(pdfedit_core_dev_init())
	{
		std::cerr << "Unable to initialize pdfedit-dev" << std::endl;
		return 1;
	}

	typedef vector<string> RefsRepr;

	po::options_description desc("Allowed options");
	desc.add_options()
		("help", "produce help message")
		("file", po::value<string>(), "Input pdf file")
		("ref", po::value<vector<string> >(), "Reference to object which should be printed e.g. \"1 0\".")
		("decode", po::value<bool>()->default_value(false), "True if streams should be decoded too.")
	;

	po::variables_map vm;
	try {
		po::store(po::parse_command_line(argc, argv, desc), vm);
		po::notify(vm);
	}catch(std::exception& e)
	{
		std::cout << "exception - " << e.what() << ". Please, check your parameters." << endl;
		return 1;
	}

		if (vm.count("help") || !vm.count("ref") || !vm.count("file") )
		{
			cout << desc << "\n";
			return 1;
		}

	string input_file = vm["file"].as<string>();
	decodeStream = vm["decode"].as<bool>(); ;

	RefContainer refs;
	RefsRepr refs_repr = vm["ref"].as<RefsRepr>();

	try {
		for (RefsRepr::const_iterator it = refs_repr.begin();
				it != refs_repr.end();
				++it)
		{
			IndiRef ref;
			utils::simpleValueFromString(*it, ref);
			if (isRefValid(&ref))
				refs.push_back(ref);
		}

		print_objects(input_file.c_str(), refs);

	}catch (std::exception& e)
	{
		cout << e.what() << "\n";
		cout << desc << "\n";
		return 1;
	}

	pdfedit_core_dev_destroy();
	return 0;
}

ext: cc

max_stars_repo_name: hackbinary/pdfedit

max_issues_repo_name: mmoselhy/PDF-X

max_forks_repo_name: mmoselhy/PDF-X


text:
#define NO_IMPORT_ARRAY

#include <pybindings.h>
#include <iostream>
#include <boost/python.hpp>

#include <container_pybindings.h>
#include <G3SuperTimestream.h>
#include <cereal/types/utility.hpp>

#include <omp.h>
#include <FLAC/stream_encoder.h>
#include <bzlib.h>

// Debugging variables for compressors.
// BZ2_VERBOSITY can range from 0 (silent) to 4.
#define SO3G_BZ2_VERBOSITY 0
#define SO3G_BZ2_BLOCKSIZE 5

static
std::string get_bz2_error_string(int err) {
	std::ostringstream s;
	switch(err) {
	case BZ_CONFIG_ERROR:
		s << "BZ_CONFIG_ERROR (library compilation issue)";
		break;
	case BZ_PARAM_ERROR:
		s <<  "BZ_PARAM_ERROR (bad blocksize, verbosity, etc)";
		break;
	case BZ_MEM_ERROR:
		s << "BZ_MEM_ERROR (not enough memory is available)";
		break;
	case BZ_OUTBUFF_FULL:
		s << "BZ_OUTBUFF_FULL (compressed data too long for buffer)";
		break;
	case BZ_OK:
		s << "BZ_OK (no problem)";
		break;
	default:
		s << "Unknown BZ error code " << err;
	}
	return s.str();
}

static
std::string get_algo_error_string(std::string var_name, int algo_code)
{
	std::ostringstream s;
	s << "No support for compression algorithm " << var_name << "=" << algo_code;
	return s.str();
}

// Split each datum in x into:
//
//    x[i] = r[i] + y[i]
//
// where r is a multiple of snap_to, -step_at <= y[i] < step_at.  The
// algorithm tries to have r[i] change slowly, meaning that r[i+1]
// will not differ from r[i] unless necessary to satisfy the
// range restriction on y[i].
//
// This is only implemented for snap_to and step_at both powers of 2!
//
// Let's allow r and x to point to same memory.
//
// This will return the number of values that y takes; 0 if there are
// no data, 1 if it's a constant value (even if that value is 0), or
// more than 1 for more than 1.
template <typename T>
static
int rebranch(int32_t *y, T *r, T *x, int n_samps, T snap_to, T step_at)
{
	T branch = 0;
        int branch_count = 0;
	int fails = 0;
	for (int i=0; i < n_samps; i++) {
		T new_branch = (x[i] + snap_to / 2) & ~(snap_to - 1);
		if (new_branch - branch >= step_at ||
		    new_branch - branch <  -step_at ||
		    branch_count == 0) {
			branch = new_branch;
                        branch_count++;
                }
		y[i] = x[i] - branch;
		// Don't update r until you use x -- they are allowed to overlap.
		r[i] = branch;
		if (y[i] < -step_at || y[i] >= step_at)
			fails++;
	}
	return branch_count;
}

FLAC__StreamEncoderWriteStatus flac_encoder_write_cb(
	const FLAC__StreamEncoder *encoder,
	const FLAC__byte buffer[],
	size_t bytes,
	unsigned samples,
	unsigned current_frame,
	void *client_data)
{
	auto ablob = (struct G3SuperTimestream::array_blob *)client_data;
	if (ablob->count + bytes > ablob->size)
		return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
	memcpy(ablob->buf + ablob->count, buffer, bytes);
	ablob->count += bytes;
	return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
}

inline int32_t* reserve_size_field(struct G3SuperTimestream::array_blob *ablob)
{
	if (ablob->size - ablob->count < sizeof(int32_t))
		return nullptr;
	auto p = (int32_t*)(ablob->buf + ablob->count);
	ablob->count += sizeof(int32_t);
	*p = ablob->count;
	return p;
}
inline bool close_size_field(struct G3SuperTimestream::array_blob *ablob, int32_t *dest)
{
	if (dest == NULL)
		return false;
	*dest = ablob->count - *dest;
	return true;
}

static
struct G3SuperTimestream::array_blob encode_array(
	PyArrayObject *array, std::vector<double> quanta,
	G3SuperTimestream::options_type options)
{
	struct G3SuperTimestream::array_blob ablob;

	// We will write (possibly) compressed data for n_det
	// detectors by n samples into a buffer.  The data block for
	// detector i starts at offsets[i] and ends at offsets[i+1].
	//
	// Each data block has the structure:
	//   [data_block] = [algo_code] [encoded_block1] [encoded_block2]
	//
	// The algo_code is a single byte drawing values from the
	// algos enum/bitmask that describes what encoded blocks will
	// follow.
	//
	// If algo_code=0 (a.k.a. ALGO_NONE) then there is one
	// encoded_block and it is simply the flat uncompressed binary
	// data for that channel.
	//
	// If algo_code != 0, then the encoded blocks will consist
	// first of a FLAC block (if algo_code & ALGO_DO_FLAC),
	// followed by a BZ2 block (if ALGO_DO_BZ) or a CONST block
	// (if ALGO_DO_CONST).
	//
	// The FLAC block has the format [length] [flac_data].  The
	// length is an int32_t giving the number of bytes in
	// flac_data.  The flac_data is the encoding of n samples of
	// single-channel 24-bit data.
	//
	// The BZ2 block has the format [length] [bz2_data].  The
	// length is an int32_t giving the number of bytes in
	// bz2_data.  The bz2_data is the encoding of n samples of the
	// full-width data (i.e. n*sizeof(dtype) bytes).
	//
	// The CONST block is a single full-width value, [datum].
	// This value represents an offset to add to the data.
	//
	// The FLAC data, if present, decode to int32_t.  The BZ2
	// data, if present, decode to int32_t or int64_t.  The CONST
	// datum, if present, represents a single int32_t or int64_t.
	// These two vectors and single offset are added together to
	// form the output integer array.
	//
	// (In practice we will only have one or the other of BZ2 and
	// CONST blocks.  In the common case that CONST would decode
	// to 0, it will not be included at all.)

	int n_chans = PyArray_SHAPE(array)[0];
	int n_samps = PyArray_SHAPE(array)[1];
	int itemsize = PyArray_ITEMSIZE(array);

	// Max bytes needed to store all the "compressed" data.
	int n_max = PyArray_NBYTES(array) + n_chans;

	// Initialize the buffer.
	ablob.buf = new char[n_max];
	ablob.size = n_max;
	ablob.count = 0;
	ablob.offsets.push_back(0);

	int32_t d[n_samps];
	const int32_t *chan_ptrs[1] = {d};

	char r[n_samps * sizeof(int64_t)];
	auto r32 = (int32_t*)r;
	auto r64 = (int64_t*)r;

	npy_intp type_num = PyArray_TYPE(array);
	char *src = (char*)(PyArray_DATA(array));
	FLAC__StreamEncoder *encoder = nullptr;
	if (options.data_algo & G3SuperTimestream::ALGO_DO_FLAC)
		encoder = FLAC__stream_encoder_new();

	int32_t M = (1 << 24);
	for (int i=0; i<n_chans; i++, src+=PyArray_STRIDES(array)[0]) {
		// Save the current ablob.buf pointer in case we need to
		// roll back the compression, which we will do if we
		// end up writing to block_limit or beyond.
		int block_start = ablob.count++;
		int block_limit = ablob.count + n_samps * itemsize;

		// Oh, the things we'll try.
		bool try_flac = (options.data_algo & G3SuperTimestream::ALGO_DO_FLAC);
		bool try_bz = (options.data_algo & G3SuperTimestream::ALGO_DO_BZ);
		bool try_const = false;

		int8_t algo_used = 0;
		bool ok = true;

		if (try_flac) {
			int32_t *flac_size = reserve_size_field(&ablob);
			int branches = 0;
			if (type_num == NPY_INT32) {
				branches = rebranch<int32_t>(d, r32, (int32_t*)src, n_samps, M, M/2);
			} else if (type_num == NPY_INT64) {
				branches = rebranch<int64_t>(d, r64, (int64_t*)src, n_samps, M, M/2);
			} else if (type_num == NPY_FLOAT32) {
				for (int j=0; j < n_samps; j++)
					r32[j] = roundf(((float*)src)[j] / quanta[i]);
				branches = rebranch<int32_t>(d, r32, r32, n_samps, M, M/2);
			} else if (type_num == NPY_FLOAT64) {
				for (int j=0; j < n_samps; j++)
					r64[j] = round(((double*)src)[j] / quanta[i]);
				branches = rebranch<int64_t>(d, r64, r64, n_samps, M, M/2);
			} else
				throw g3supertimestream_exception("Invalid array type encountered.");
			if (branches <= 1) {
				try_bz = false;
				try_const = true;
			}

			FLAC__stream_encoder_set_channels(encoder, 1);
			FLAC__stream_encoder_set_bits_per_sample(encoder, 24);
			FLAC__stream_encoder_set_compression_level(encoder, options.flac_level);
			FLAC__stream_encoder_init_stream(
				encoder, flac_encoder_write_cb, NULL, NULL, NULL, (void*)(&ablob));
			if (!FLAC__stream_encoder_process(encoder, chan_ptrs, n_samps) ||
			    !FLAC__stream_encoder_finish(encoder))
				throw g3supertimestream_exception("FLAC encoding fail.");
			ok = close_size_field(&ablob, flac_size);
			algo_used |= G3SuperTimestream::ALGO_DO_FLAC;
		} else {
			memcpy(r, src, n_samps * itemsize);
			if (type_num == NPY_FLOAT32) {
				for (int j=0; j<n_samps; j++)
					((int32_t*)r)[j] = roundf(((float*)r)[j] / quanta[i]);
			} else if (type_num == NPY_FLOAT64) {
				for (int j=0; j<n_samps; j++)
					((int64_t*)r)[j] = round(((double*)r)[j] / quanta[i]);
			}
		}

		if (ok && try_const) {
			// Take care in the cute handling of special
			// case r[0] == 0.  If you naively leave out
			// the ALGO_DO_CONST bit, and if FLAC
			// compression was disabled, then you end up
			// storing algo=0, which the decoder will
			// interpret as raw format, not zero-byte
			// ultracompression representing all zeros!
			int n_copy = 0;
			switch (type_num) {
			case NPY_FLOAT32:
			case NPY_INT32:
				if (r32[0] != 0 || algo_used == 0)
					n_copy = sizeof(int32_t);
				break;
			case NPY_FLOAT64:
			case NPY_INT64:
				if (r64[0] != 0 || algo_used == 0)
					n_copy = sizeof(int64_t);
				break;
			}
			if (ablob.count + n_copy < block_limit) {
				if (n_copy > 0) {
					memcpy(ablob.buf + ablob.count, r, n_copy);
					ablob.count += n_copy;
					algo_used |= G3SuperTimestream::ALGO_DO_CONST;
				}
			} else {
				ok = false;
			}
		}
		if (ok && try_bz) {
			int32_t *bz_size = reserve_size_field(&ablob);
			algo_used |= G3SuperTimestream::ALGO_DO_BZ;
			if (ablob.count < block_limit) {
				unsigned int n_write = block_limit - ablob.count;
				int err = BZ2_bzBuffToBuffCompress(
					ablob.buf + ablob.count, &n_write, r, n_samps * itemsize,
					SO3G_BZ2_BLOCKSIZE, SO3G_BZ2_VERBOSITY, options.bz2_workFactor);
				if (err == BZ_OUTBUFF_FULL) {
					// Too long, don't bother.
					ok = false;
				} else if (err != BZ_OK) {
					throw g3supertimestream_exception(get_bz2_error_string(err));
				} else {
					ablob.count += n_write;
					ok = close_size_field(&ablob, bz_size);
				}
			} else
				ok = false;
		}

		if (ok && ablob.count < block_limit) {
			// That went well.
			ablob.buf[block_start] = algo_used;
		} else {
			// Bail out into raw copy.
			ablob.buf[block_start] = G3SuperTimestream::ALGO_NONE;
			memcpy(ablob.buf + block_start + 1, src, n_samps * itemsize);
			ablob.count = block_limit;
		}
		ablob.offsets.push_back(ablob.count);
	}
	if (encoder != nullptr)
		FLAC__stream_encoder_delete(encoder);

	return ablob;
}

/* G3SuperTimestream */

std::string G3SuperTimestream::Description() const
{
	std::ostringstream s;
	s << "G3SuperTimestream("
	  << names.size() << ", " << times.size() << ")";
	return s.str();
}

std::string G3SuperTimestream::Summary() const
{
	return Description();
}

template <class A> void G3SuperTimestream::load(A &ar, unsigned v)
{
	G3_CHECK_VERSION(v);
	using namespace cereal;
	ar & make_nvp("parent", base_class<G3FrameObject>(this));

        // Compression options.
	ar & make_nvp("flac_level", options.flac_level);
	ar & make_nvp("bz2_workFactor", options.bz2_workFactor);

	ar & make_nvp("times_algo", options.times_algo);
	if (options.times_algo == ALGO_NONE) {
		ar & make_nvp("times", times);
	} else if (options.times_algo == ALGO_DO_BZ) {
		int n_samps;
		unsigned int max_bytes;
		ar & make_nvp("n_samps", n_samps);
		ar & make_nvp("comp_bytes", max_bytes);

                char _buf[max_bytes];
                char *buf = (char*)_buf;
		ar & make_nvp("times_data", binary_data(buf, max_bytes));

		std::vector<int64_t> ints(n_samps);
		unsigned int n_decomp = n_samps * sizeof(ints[0]);
		int err = BZ2_bzBuffToBuffDecompress(
                    (char*)&ints[0], &n_decomp, buf, max_bytes,
			     1, SO3G_BZ2_VERBOSITY);
		if (err != BZ_OK)
			throw g3supertimestream_exception(get_bz2_error_string(err));
		times = G3VectorTime(ints.begin(), ints.end());
	} else
		throw g3supertimestream_exception(
			get_algo_error_string("times_algo", options.times_algo));

	ar & make_nvp("names", names);

	// Read the desc.
        ar & make_nvp("type_num", desc.type_num);
        ar & make_nvp("ndim", desc.ndim);
	ar & make_nvp("shape", desc.shape);
        ar & make_nvp("nbytes", desc.nbytes);

	ar & make_nvp("data_algo", options.data_algo);
	if (options.data_algo == ALGO_NONE) {
		array = (PyArrayObject*)
			PyArray_SimpleNew(desc.ndim, desc.shape, desc.type_num);
		ar & make_nvp("data_raw", binary_data((char*)PyArray_DATA(array),
						      PyArray_NBYTES(array)));
	} else {
		// Read the flacblock
		ablob = new struct array_blob;
		ar & make_nvp("quanta", quanta);
		ar & make_nvp("offsets", ablob->offsets);
		ar & make_nvp("payload_bytes", ablob->count);
		ablob->buf = new char[ablob->count];
		ar & make_nvp("payload", binary_data(ablob->buf, ablob->count));
	}
	dataful = true;
	float_mode = (desc.type_num == NPY_FLOAT32 ||
		      desc.type_num == NPY_FLOAT64);
}

template <class A> void G3SuperTimestream::save(A &ar, unsigned v) const
{
	using namespace cereal;
	ar & make_nvp("parent", base_class<G3FrameObject>(this));

        // Compression options.
	ar & make_nvp("flac_level", options.flac_level);
	ar & make_nvp("bz2_workFactor", options.bz2_workFactor);

	if (options.times_algo == ALGO_DO_BZ) {
		// Try to bz2 compress.  Convert to a vector of int64_t first.
		auto time_ints = vector<long>(times.begin(), times.end());
		int n_samps = time_ints.size();
		unsigned int max_bytes = n_samps * sizeof(time_ints[0]);

                char _buf[max_bytes];
		char *buf = _buf;

		int err = BZ2_bzBuffToBuffCompress(
			buf, &max_bytes, (char*)&time_ints[0], max_bytes,
			SO3G_BZ2_BLOCKSIZE, SO3G_BZ2_VERBOSITY, options.bz2_workFactor);
		if (err == BZ_OUTBUFF_FULL) {
			// Fallback to no-compression.
			ar & make_nvp("times_algo", (int8_t)ALGO_NONE);
			ar & make_nvp("times", times);
		} else if (err != BZ_OK) {
			throw g3supertimestream_exception(get_bz2_error_string(err));
		} else {
			ar & make_nvp("times_algo", (int8_t)ALGO_DO_BZ);
			ar & make_nvp("n_samps", n_samps);
			ar & make_nvp("comp_bytes", max_bytes);
			ar & make_nvp("times_data", binary_data(buf, max_bytes));
		}
	} else {
		ar & make_nvp("times_algo", (int8_t)ALGO_NONE);
		ar & make_nvp("times", times);
	}

	ar & make_nvp("names", names);

	// Write the desc.
        ar & make_nvp("type_num", desc.type_num);
        ar & make_nvp("ndim", desc.ndim);
	ar & make_nvp("shape", desc.shape);
        ar & make_nvp("nbytes", desc.nbytes);

	ar & make_nvp("data_algo", options.data_algo);
	if (options.data_algo == ALGO_NONE) {
		if (array == nullptr)
			throw g3supertimestream_exception(
				"Unexpected state: array is NULL.");
		// Check the endianness
		//auto this_descr = PyArray_DESCR(array);
		if (!PyArray_EquivByteorders(PyArray_DESCR(array)->byteorder, NPY_LITTLE))
			throw g3supertimestream_exception(
				"The byte_order of the data array is not acceptable.");
		// Might as well use numpy to repack it properly...
		PyArrayObject *contig = PyArray_GETCONTIGUOUS(array);
		ar & make_nvp("data_raw", binary_data((char*)PyArray_DATA(contig),
						      PyArray_NBYTES(contig)));
		Py_DECREF((PyObject*)contig);
	} else {
		struct array_blob *_ablob = ablob;
		if (_ablob == nullptr) {
			// Encode to a copy.
			_ablob = new struct array_blob;
			*_ablob = encode_array(array, quanta, options);
		}

		// Write the ablobblock
		ar & make_nvp("quanta", quanta);
		ar & make_nvp("offsets", _ablob->offsets);
		ar & make_nvp("payload_bytes", _ablob->count);
		ar & make_nvp("payload", binary_data(_ablob->buf, _ablob->count));

		if (_ablob != ablob) {
			delete _ablob->buf;
			delete _ablob;
		}
	}
}

bool G3SuperTimestream::Encode() {
	if (array == nullptr)
		return false;

	// Compress the array data.
	if (options.data_algo == ALGO_NONE)
		return false;
	else {
		ablob = new struct array_blob;
		*ablob = encode_array(array, quanta, options);
		Py_XDECREF(array);
		array = nullptr;
	}
	return true;
}

struct flac_helper {
	int bytes_remaining;
	char *src;
	char *dest;
};

FLAC__StreamDecoderReadStatus read_callback(
	const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
{
	auto fh = (struct flac_helper *)client_data;
	/* printf(" ... read %i (remaining: %i)\n", *bytes, fh->bytes_remaining); */
	if (fh->bytes_remaining == 0) {
		*bytes = 0;
		return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
	}
	if (fh->bytes_remaining < *bytes)
		*bytes = fh->bytes_remaining;
	memcpy(buffer, fh->src, *bytes);
	fh->bytes_remaining -= *bytes;
	fh->src += *bytes;
	return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}

template <typename T>
FLAC__StreamDecoderWriteStatus write_callback_int(
	const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data)
{
	auto fh = (struct flac_helper *)client_data;
	int n = frame->header.blocksize;
	for (int i=0; i<n; i++)
		((T*)fh->dest)[i] = buffer[0][i];
	fh->dest += n * sizeof(T);
	return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}

static void flac_decoder_error_cb(const FLAC__StreamDecoder *decoder,
				  FLAC__StreamDecoderErrorStatus status, void *client_data)
{

	switch (status) {
	case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
		printf("FLAC decoding error (lost sync)");
	case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
		printf("FLAC decoding error (bad header)");
	case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
		printf("FLAC decoding error (CRC mismatch)");
	case FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM:
		printf("FLAC decoding error (unparseable stream)");
	default:
		printf("FLAC decoding error (%d)", status);
	}
}

template <typename T>
void expand_branch(struct flac_helper *fh, int n_bytes, int nsamps,
	char *temp)
{
	bool own_temp = (temp == nullptr);
	unsigned int temp_size = nsamps * sizeof(T);
	if (own_temp)
		temp = new char[temp_size];

	int err = BZ2_bzBuffToBuffDecompress(
		temp, &temp_size, fh->src, n_bytes,
		1, SO3G_BZ2_VERBOSITY);
	if (err != BZ_OK)
		throw g3supertimestream_exception(get_bz2_error_string(err));
	// Add it in ...
	for (int i=0; i<nsamps; i++)
		((T*)fh->dest)[i] += ((T*)temp)[i];

	if (own_temp)
		delete temp;
}

template <typename T>
void broadcast_val(struct flac_helper *fh, int nsamps)
{
	T val = *((T*)(fh->src));
	T *dest = (T*)fh->dest;
	for (int i=0; i<nsamps; i++)
		dest[i] += val;
}

inline int32_t _read_size(struct flac_helper *fh)
{
	auto v = *((int32_t*)(fh->src));
	fh->src += sizeof(v);
	return v;
}

bool G3SuperTimestream::Decode()
{
	if (ablob == nullptr)
		return false;

	if (options.data_algo == ALGO_NONE)
		throw g3supertimestream_exception(
			"Decode called with ablob buffer but data_algo=0.");

	array = (PyArrayObject*)
		PyArray_ZEROS(desc.ndim, desc.shape, desc.type_num, 0);

	FLAC__StreamDecoderWriteCallback this_write_callback;
	void (*expand_func)(struct flac_helper *, int, int, char*) = nullptr;
	void (*broadcast_func)(struct flac_helper *, int) = nullptr;
	int elsize = 0;

	switch (desc.type_num) {
	case NPY_INT32:
	case NPY_FLOAT32:
		this_write_callback = &write_callback_int<int32_t>;
		expand_func = expand_branch<int32_t>;
		broadcast_func = broadcast_val<int32_t>;
		elsize = sizeof(int32_t);
		break;
	case NPY_INT64:
	case NPY_FLOAT64:
		this_write_callback = &write_callback_int<int64_t>;
		expand_func = expand_branch<int64_t>;
		broadcast_func = broadcast_val<int64_t>;
		elsize = sizeof(int64_t);
		break;
	default:
		throw g3supertimestream_exception("Invalid array type encountered.");
	}

#pragma omp parallel
	{

	// Each OMP thread needs its own workspace, FLAC decoder, and helper structure
	char temp[PyArray_SHAPE(array)[1] * elsize + 1];
	FLAC__StreamDecoder *decoder = nullptr;
	struct flac_helper helper;

#pragma omp for
	for (int i=0; i<desc.shape[0]; i++) {
		char* this_data = (char*)PyArray_DATA(array) + PyArray_STRIDES(array)[0]*i;

		// Cue up this detector's data and read the algo code.
		helper.src = ablob->buf + ablob->offsets[i];
		int8_t algo = *(helper.src++);

		if (algo == ALGO_NONE) {
			memcpy(this_data, helper.src, PyArray_SHAPE(array)[1] * elsize);
		}
		if (algo & ALGO_DO_FLAC) {
			if (decoder == nullptr)
				decoder = FLAC__stream_decoder_new();
			helper.bytes_remaining = _read_size(&helper);
			helper.dest = this_data;

			FLAC__stream_decoder_init_stream(
				decoder, read_callback, NULL, NULL, NULL, NULL,
				*this_write_callback, NULL, flac_decoder_error_cb,
				(void*)&helper);
			FLAC__stream_decoder_process_until_end_of_stream(decoder);
			FLAC__stream_decoder_finish(decoder);
		}

		// A bz2 field of slow offsets?
		if (algo & ALGO_DO_BZ) {
			helper.bytes_remaining = _read_size(&helper);
			helper.dest = this_data;
			expand_func(&helper,
				    helper.bytes_remaining,
				    PyArray_SHAPE(array)[1], (char*)temp);
		}

		// Single flat offset?
		if (algo & ALGO_DO_CONST) {
			helper.dest = this_data;
			broadcast_func(&helper, PyArray_SHAPE(array)[1]);
		}

		// Now convert for precision.
		if (desc.type_num == NPY_FLOAT32) {
			auto src = (int32_t*)this_data;
			auto dest = (float*)this_data;
			for (int j=0; j<PyArray_SHAPE(array)[1]; j++)
				dest[j] = (float)src[j] * quanta[i];
		} else if (desc.type_num == NPY_FLOAT64) {
			auto src = (int64_t*)this_data;
			auto dest = (double*)this_data;
			for (int j=0; j<PyArray_SHAPE(array)[1]; j++)
				dest[j] = src[j] * quanta[i];
		}
	}
	if (decoder != nullptr)
		FLAC__stream_decoder_delete(decoder);
	} // omp parallel

	// Destroy the flac bundle.
	delete ablob->buf;
	delete ablob;
	ablob = nullptr;

	return true;
}

int G3SuperTimestream::Options(
	int enable, int flac_level, int bz2_workFactor,
	int data_algo, int times_algo)
{
	// Global enable / disable first.
	if (enable == 0) {
		options.data_algo = 0;
		options.times_algo = 0;
	} else if (enable > 0) {
		options.data_algo = ALGO_DO_FLAC | ALGO_DO_BZ;
		options.times_algo = ALGO_DO_BZ;
	}

	if (data_algo >= 0)
		options.data_algo = data_algo;
	if (times_algo >= 0)
		options.times_algo = times_algo;

	if (flac_level >= 0)
		options.flac_level = flac_level;
	if (bz2_workFactor >= 0)
		options.bz2_workFactor = bz2_workFactor;

	return 0;
}

template <typename T>
static
void _apply_cals_typed(PyArrayObject *array, std::vector<double> cals)
{
	for (int i=0; i<PyArray_SHAPE(array)[0]; i++) {
		auto dest = (T*)((char*)PyArray_DATA(array) + PyArray_STRIDES(array)[0] * i);
		for (int j=0; j<PyArray_SHAPE(array)[1]; j++)
			dest[j] *= cals[i];
	}
}

void _apply_cals(PyArrayObject *array, std::vector<double> cals)
{
	switch (PyArray_TYPE(array)) {
	case NPY_FLOAT32:
		_apply_cals_typed<float>(array, cals);
		break;
	case NPY_FLOAT64:
		_apply_cals_typed<double>(array, cals);
		break;
	default:
		throw g3supertimestream_exception("Unexpected dtype!");
	}
}

void G3SuperTimestream::Calibrate(std::vector<double> rescale)
{
	if (rescale.size() != names.size())
		throw g3supertimestream_exception(
			"Rescale vector has unexpected length.");
	if (float_mode) {
		// Modification to the calibration.
		if (array)
			_apply_cals(array, rescale);
		for (int i=0; i<quanta.size(); i++)
			quanta[i] *= rescale[i];
	} else {
		// Transition to float_mode.  If holding integer
		// array, convert it.
		if (dataful) {
			switch(desc.type_num) {
			case NPY_INT32:
				desc.type_num = NPY_FLOAT32;
				break;
			case NPY_INT64:
				desc.type_num = NPY_FLOAT64;
				break;
			default:
				throw g3supertimestream_exception("Unexpected dtype!");
			}
			if (array) {
				auto new_array = (PyArrayObject*)PyArray_FromAny(
					(PyObject*)array, PyArray_DescrFromType(desc.type_num),
					0, 0, NPY_ARRAY_FORCECAST, NULL);
				if (new_array == nullptr)
					throw g3supertimestream_exception(
						"Failed to allocate float array.");
				_apply_cals(new_array, rescale);
				Py_DECREF(array);
				array = new_array;
			}
		}
		float_mode = true;
		quanta = rescale;
	}
}

G3SuperTimestream::G3SuperTimestream()
{
	array = nullptr;
	ablob = nullptr;
	float_mode = false;
	dataful = false;
	if (!PyArray_EquivByteorders(NPY_NATIVE, NPY_LITTLE))
		throw g3supertimestream_exception(
			"This class hasn't been trained on BIG-endian machines.!");

	// Set defaults.
	Options(1, 1, 30, -1, -1);
}

G3SuperTimestream::~G3SuperTimestream()
{
	if (array != nullptr)
		Py_XDECREF(array);
	if (ablob != nullptr) {
		delete ablob->buf;
		delete ablob;
	}
}

G3SuperTimestream::G3SuperTimestream(
	const G3VectorString &names_, const G3VectorTime &times_)
	: G3SuperTimestream()
{
	names = G3VectorString(names_);
	times = G3VectorTime(times_);
}

static
void safe_set_data(G3SuperTimestream &self, const bp::object object_in);
static
void safe_set_quanta(G3SuperTimestream &self, std::vector<double> quanta);

G3SuperTimestream::G3SuperTimestream(
	const G3VectorString &names_, const G3VectorTime &times_,
	const bp::object &data_)
	: G3SuperTimestream(names_, times_)
{
	safe_set_data(*this, data_);
}

G3SuperTimestream::G3SuperTimestream(
	const G3VectorString &names_, const G3VectorTime &times_,
	const bp::object &data_, const std::vector<double> &quanta_)
	: G3SuperTimestream(names_, times_)
{
	safe_set_quanta(*this, quanta_);
	safe_set_data(*this, data_);
}

static
void safe_set_times(G3SuperTimestream &self, G3VectorTime _times)
{
	// Only allow this if it doesn't upset consistency.  We will
	// assume that, coming in, we're internally consistent.
	if (_times.size() != self.times.size() && self.times.size() != 0) {
		std::ostringstream s;
		s << "Cannot set .times because it conflicts with "
		  << "the established number of samples (" << self.times.size()
		  << ").";
		throw g3supertimestream_exception(s.str());
	}
	self.times = _times;
}

static
void safe_set_names(G3SuperTimestream &self, G3VectorString _names)
{
	// Only allow this if it doesn't upset consistency.  We will
	// assume that, coming in, we're internally consistent.
	if (_names.size() != self.names.size() && self.names.size() != 0) {
		std::ostringstream s;
		s << "Cannot set .names because it conflicts with "
		  << "the established number of channels (" << self.names.size()
		  << ").";
		throw g3supertimestream_exception(s.str());
	}
	self.names = _names;
}

static
void safe_set_data(G3SuperTimestream &self, const bp::object object_in)
{
	// Note this function, as invoked here, might return a
	// reference or create a new array.
	PyObject *ob = PyArray_FromAny(object_in.ptr(), NULL, 0, 0, 0, NULL);
	if (ob == NULL)
		throw g3supertimestream_exception("Could not decode array.");

	PyArrayObject *_array = reinterpret_cast<PyArrayObject*>(ob);

	if (PyArray_NDIM(_array) != 2) {
		Py_XDECREF(ob);
		throw g3supertimestream_exception("Bad ndim.");
	}
	if (PyArray_DIMS(_array)[0] != self.names.size()) {
		Py_XDECREF(ob);
		throw g3supertimestream_exception("Bad shape[0].");
	}
	if (PyArray_DIMS(_array)[1] != self.times.size()) {
		Py_XDECREF(ob);
		throw g3supertimestream_exception("Bad shape[1].");
	}
	if (!PyArray_EquivByteorders(PyArray_DESCR(_array)->byteorder, NPY_LITTLE)) {
		//There are other ways to deal with endianness.
		Py_XDECREF(ob);
		throw g3supertimestream_exception("Bad endianness.");
	}

	bool is_floaty = false;
	switch(PyArray_TYPE(_array)) {
	case NPY_FLOAT32:
	case NPY_FLOAT64:
		is_floaty = true;
		break;
	case NPY_INT32:
	case NPY_INT64:
		break;
	default:
		Py_XDECREF(ob);
		throw g3supertimestream_exception("Forbidden dtype.");
	}

	if (is_floaty) {
		// quanta has to be set already.
		if (self.quanta.size() != PyArray_DIMS(_array)[0])
			throw g3supertimestream_exception(
				"User must set .quanta before loading float array.");
	} else {
		if (self.quanta.size() != 0)
			throw g3supertimestream_exception(
				"The .quanta must be empty when loading integer array.");
	}

	// Clear cached array or compressed data.
	if (self.array) {
		Py_XDECREF((PyObject*)self.array);
		self.array = nullptr;
	}
	if (self.ablob) {
		delete self.ablob->buf;
		delete self.ablob;
		self.ablob = nullptr;
	}

	self.dataful = true;
	self.float_mode = is_floaty;

	self.desc.ndim = PyArray_NDIM(_array);
	self.desc.type_num = PyArray_TYPE(_array);

	self.desc.nbytes = PyArray_NBYTES(_array);
	for (int i=0; i<self.desc.ndim; i++)
		self.desc.shape[i] = PyArray_DIMS(_array)[i];

	self.array = _array;
}

static
bp::object safe_get_data(G3SuperTimestream &self)
{
	if (self.array == nullptr)
		self.Decode();
	return bp::object(bp::handle<>(bp::borrowed(reinterpret_cast<PyObject*>(self.array))));
}

static
bp::object safe_get_dtype(G3SuperTimestream &self)
{
	PyObject *d;
	if (self.array == nullptr) {
		d = reinterpret_cast<PyObject*>(
			PyArray_DescrFromType(self.desc.type_num));
	} else {
		d = reinterpret_cast<PyObject*>(PyArray_DESCR(self.array));
		Py_XINCREF(d);
	}
	return bp::object(bp::handle<>(d));
}

static
bp::object safe_get_quanta(G3SuperTimestream &self)
{
	if (!self.float_mode)
		return bp::object();

	npy_intp shape[1] = {(npy_intp)self.quanta.size()};
	auto output = (PyArrayObject *)PyArray_SimpleNew(1, shape, NPY_FLOAT64);
	memcpy(PyArray_DATA(output), &self.quanta[0], shape[0] * sizeof(self.quanta[0]));
	return bp::object(bp::handle<>(reinterpret_cast<PyObject*>(output)));
}

static
void safe_set_quanta(G3SuperTimestream &self, std::vector<double> quanta)
{
	// Only allowed to set quanta directly if data is not present.
	if (!self.dataful)
		self.Calibrate(quanta);
	else
		throw g3supertimestream_exception(
			"The .quanta cannot be set directly once .data is set.  Use .calibrate().");
}

//Copies data out of a flat buffer, creating the numpy array along the way.
bool G3SuperTimestream::SetDataFromBuffer(void* buf, int ndim, int shape[], int typenum,
					 std::pair<int,int> sample_range)
{
	if (ndim != 2)
		throw g3supertimestream_exception(
			"2d arrays only please");

	// Create a new numpy array for this, allowing for slice in
	// second dimension..
	int n_samps = sample_range.second - sample_range.first;
	npy_intp shape_[2] = {shape[0], n_samps};
	auto array_ = (PyArrayObject*)PyArray_EMPTY(ndim, shape_, typenum, 0);
	bp::object array_ob =
		bp::object(bp::handle<>((reinterpret_cast<PyObject*>(array_))));

	for (int i=0; i<shape[0]; i++) {
		memcpy((char*)PyArray_DATA(array_) + PyArray_STRIDES(array_)[0] * i,
		       (char*)buf + (sample_range.first + shape[1] * i) * PyArray_ITEMSIZE(array_),
		       PyArray_STRIDES(array_)[1] * n_samps);
	}

	safe_set_data(*this, array_ob);
	return true;
}

// Assist with testing the pure C++ interface
static
G3SuperTimestreamPtr test_cxx_interface(int nsamps, int first, int second)
{
	int shape[2] = {3, nsamps};
	int typenum = NPY_INT32;
	int32_t buf[shape[0] * shape[1]] = {0};

	auto times = G3VectorTime();
	for (int i=first; i<second; i++)
		times.push_back(G3Time::Now());
	const char *chans_[] = {"a", "b", "c"};
	auto chans = G3VectorString(chans_, std::end(chans_));

	auto ts = G3SuperTimestreamPtr(
		new G3SuperTimestream(chans, times));

	for (int i=first; i<second; i++)
		buf[i] = 77;

	ts->SetDataFromBuffer((void*)buf, 2, shape, typenum,
			      std::pair<int,int>(first, second));

	return ts;
}

G3_SPLIT_SERIALIZABLE_CODE(G3SuperTimestream);

static void translate_ValueError(g3supertimestream_exception const& e)
{
	PyErr_SetString(PyExc_ValueError, e.msg_for_python().c_str());
}

PYBINDINGS("so3g")
{
	bp::docstring_options local_docstring_options(true, false);

	EXPORT_FRAMEOBJECT(G3SuperTimestream, init<>(), "G3SuperTimestream()\n\n"
		"  Construct with names and times uninitialized.")
		.def(bp::init<const G3VectorString&, const G3VectorTime&>(
			     "G3SuperTimestream(names, times)\n"
			     "  Construct with names and times initialized."))
		.def(bp::init<const G3VectorString&, const G3VectorTime&,
		     bp::object&>(
			     "G3SuperTimestream(names, times, data)\n\n"
			     "  Construct with integer data."))
		.def(bp::init<const G3VectorString&, const G3VectorTime&,
		     bp::object&, const std::vector<double>&>(
			     "G3SuperTimestream(names, times, data, quanta)\n\n"
			     "  Construct with float data."))
		.add_property("times", &G3SuperTimestream::times, &safe_set_times,
			      "Vector of timestamps (G3VectorTime).")
		.add_property("names", &G3SuperTimestream::names, &safe_set_names,
			      "Vector of channel names.")
		.add_property("data", &safe_get_data, &safe_set_data,
			      "Data array.")
		.add_property("quanta", &safe_get_quanta, &safe_set_quanta,
			      "Quanta (if float mode).")
		.add_property("dtype", &safe_get_dtype, "Numpy dtype of enclosed array.")
		.def("encode", &G3SuperTimestream::Encode, "Compress.")
		.def("decode", &G3SuperTimestream::Decode, "Decompress.")
		.def("calibrate", &G3SuperTimestream::Calibrate,
		     "calibrate(cal_factors)\n\n"
                     "Apply per-channel scale factors.  Note this puts you in float mode\n"
		     "(if you're not already) and modifies both .quanta and .data.")
		.def("options", &G3SuperTimestream::Options,
		     (bp::arg("enable")=-1,
		      bp::arg("flac_level")=-1, bp::arg("bz2_workFactor")=-1,
		      bp::arg("data_algo")=-1, bp::arg("times_algo")=-1),
		     "Set compression options.")
		;
	register_pointer_conversions<G3SuperTimestream>();

	bp::register_exception_translator<g3supertimestream_exception>(&translate_ValueError);
	bp::def("test_g3super", test_cxx_interface);
}

ext: cxx


text:
//
// updater_impl.hpp
// ~~~~~~~~~~~~~~~~
//
// Copyright (c) 2011 Jack (jack.wgm@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// path LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// * $Id: updater_impl.hpp 49 2011-07-15 03:00:34Z jack $
//

#ifndef __UPDATER_IMPL_H__
#define __UPDATER_IMPL_H__

#pragma once

#include "md5.hpp"
#include "tinyxml.h"
#include "updater.hpp"

#include <stdio.h>
#include <string.h>

#include <iostream>
#include <map>
#include <string>
#include <vector>

#include <boost/asio.hpp>
using boost::asio::ip::tcp;

#include <boost/weak_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/function.hpp>
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
#include <boost/algorithm/string.hpp>

class updater_impl
{
public:
	typedef boost::function<void (std::string file,
		int count, int index)> fun_check_files_callback;
	typedef boost::function<void (std::string file,
		int count, int index)> fun_update_files_process;
	typedef boost::function<void (std::string file, int count, int index,
		int total_size, int total_read_bytes, int file_size, int read_bytes)> fun_down_load_callback;

	typedef struct
	{
		std::string name;             // 文件名.
		std::string md5;              // 文件的MD5值.
		std::string filehash;         // 压缩文件的HASH.
		std::string url;              // 下载的url, 默认为空.
		std::string command;          // 命令.
		std::string compress;         // 压缩选项.
		bool check;							// 文件检查选项, 如果为ture, 则表示不比较MD5, 只当文件不存在时才更新.
		boost::int64_t size;         // 大小.
	} xml_node_info;

	typedef std::map<std::string, xml_node_info> info_map;

public:
	updater_impl(void);
	virtual ~updater_impl(void);

public:
	bool start(const std::string& url, fun_check_files_callback fc, fun_down_load_callback dl,
		fun_check_files_callback cf, fun_update_files_process uf, std::string setup_path);
	bool check_update(const std::string& url, const std::string& setup_path);
	void stop();
	void pause();
	void resume();
	updater::result_type result();

protected:
	// 更新.
	void update_files();
	bool parser_xml_file(const std::string& file);

	// 文件下载.
	bool file_down_load(const std::string& u, const std::string& file,
		const std::string& extera_header = "", boost::int64_t size = -1);
	void down_load_callback(std::string file, int count, int index,
		int total_size, int total_read_bytes, int file_size, int read_bytes);

	// 最后修改时间.
	std::string make_http_last_modified(const std::string& file);
	std::string last_modified(const std::string& file);

private:
	fun_check_files_callback m_setup_file_check;
	fun_down_load_callback m_down_load_fun;
	fun_check_files_callback m_fun_check_files;
	fun_update_files_process m_update_files_fun;
	bool m_is_downloading;
	std::string m_url;
	std::string m_setup_path;
	boost::thread m_update_thrd;
	info_map m_update_file_list;
	info_map m_need_update_list;
	boost::uint64_t m_upfile_total_size;
	int m_current_index;
	int m_total_read_bytes;
	boost::asio::io_service m_io_service;
	boost::weak_ptr<tcp::socket> m_sock;
	bool m_abort;
	bool m_paused;
	updater::result_type m_result;
};

#endif // __UPDATER_IMPL_H__

ext: hpp


text:
#include "logger.h"
#include <boost/log/common.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/attributes/timer.hpp>
#include <boost/log/attributes/named_scope.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>

namespace logging = boost::log;
namespace sinks = boost::log::sinks;
namespace attrs = boost::log::attributes;
namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace keywords = boost::log::keywords;

typedef boost::unique_lock<boost::shared_mutex> write_lock;
using boost::shared_ptr;
static src::severity_logger< Logger::Severity_Level > slg;
static boost::shared_mutex mutex;

template< typename CharT, typename TraitsT >
inline std::basic_ostream< CharT, TraitsT >& operator<< (
	std::basic_ostream< CharT, TraitsT >& strm, Logger::Severity_Level lvl)
{
	static const char* const str[] =
	{
		"DEBUG",
		"INFO",
		"EVENT",
		"ALARM",
		"ERROR",
	};
	if (static_cast<std::size_t>(lvl) < (sizeof(str) / sizeof(*str)))
		strm << str[lvl];
	else
		strm << static_cast<int>(lvl);
	return strm;
}

Logger::Logger()
{

}

Logger::~Logger()
{

}

void Logger::initial()
{
	//logging::add_console_log(std::clog, keywords::format = "%TimeStamp%: %Message%");

	logging::add_file_log
	(
		"sample.log",
		keywords::open_mode= std::ios::app,
		keywords::rotation_size = 10*1024*1024,
		keywords::min_free_space = 20*1024*1024,
		keywords::filter = expr::attr< Severity_Level >("Severity") >= DEBUG,
		keywords::format = expr::stream
		<< expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "<%Y-%m-%d %H:%M:%S>")
		<< "[" << expr::attr< Severity_Level >("Severity")
		<< "] " << expr::message
	);
	logging::add_common_attributes();
	logging::core::get()->add_thread_attribute("Scope", attrs::named_scope());
	slg.add_attribute("Uptime", attrs::timer());
}
void Logger::log_write(Severity_Level level, std::string func_name, std::string os)
{
	write_lock(mutex);
	BOOST_LOG_SEV(slg, level) << "["<<func_name<<"] "<<os;
}
#define LOG_W(level,param) Logger::log_write(level, __FUNCTION__, param);

int main(int argc, char* argv[])
{
	Logger::initial();
	LOG_W(Logger::Severity_Level::DEBUG, std::string("hello3")+ std::to_string(1));
	return 0;
}