void	InstrumentTest::testInstrument() {
	debug(LOG_DEBUG, DEBUG_LOG, 0, "testInstrument() begin");
	ConfigurationPtr	config = Configuration::get(dbfilename);
	Database	database = config->database();

	// create an instrument
	Instrument	instrument(database, "BLUBB");

	// add a few components
	InstrumentComponentPtr	camera = InstrumentComponentPtr(
		new InstrumentComponentDirect(DeviceName::Camera,
			DeviceName("camera:simulator/camera"), 7, "localhost"));
	instrument.add(camera);
	InstrumentComponentPtr	ccd = InstrumentComponentPtr(
		new InstrumentComponentDerived(DeviceName::Ccd,
			instrument, DeviceName::Camera, 5));
	instrument.add(ccd);

	// check instrument
	CPPUNIT_ASSERT(instrument.name() == "BLUBB");

	// has method
	debug(LOG_DEBUG, DEBUG_LOG, 0, "test 'has' method");
	CPPUNIT_ASSERT(instrument.has(DeviceName::Camera));
	CPPUNIT_ASSERT(instrument.has(DeviceName::Ccd));

	// component_type method
	debug(LOG_DEBUG, DEBUG_LOG, 0, "test 'component_type' method");
	CPPUNIT_ASSERT(instrument.component_type(DeviceName::Camera)
		== InstrumentComponent::direct);
	CPPUNIT_ASSERT(instrument.component_type(DeviceName::Ccd)
		== InstrumentComponent::derived);

	// devicename method
	debug(LOG_DEBUG, DEBUG_LOG, 0, "test 'devicename' method");
	CPPUNIT_ASSERT(instrument.devicename(DeviceName::Camera)
		== DeviceName("camera:simulator/camera"));
	debug(LOG_DEBUG, DEBUG_LOG, 0, "ccd device: %s",
		instrument.devicename(DeviceName::Ccd).toString().c_str());
	CPPUNIT_ASSERT(instrument.devicename(DeviceName::Ccd)
		== DeviceName("camera:simulator/camera"));

	// name method
	debug(LOG_DEBUG, DEBUG_LOG, 0, "test 'name' method");
	debug(LOG_DEBUG, DEBUG_LOG, 0, "name(camera) = %s",
		instrument.name(DeviceName::Camera).c_str());
	CPPUNIT_ASSERT(instrument.name(DeviceName::Camera)
		== DeviceName("camera:simulator/camera").toString());

	debug(LOG_DEBUG, DEBUG_LOG, 0, "name(ccd) = %s",
		instrument.name(DeviceName::Ccd).c_str());
	CPPUNIT_ASSERT(instrument.name(DeviceName::Ccd) == "camera");

	// unit method
	debug(LOG_DEBUG, DEBUG_LOG, 0, "test 'unit' method");
	CPPUNIT_ASSERT(instrument.unit(DeviceName::Camera) == 7);
	CPPUNIT_ASSERT(instrument.unit(DeviceName::Ccd) == 5);
	
	debug(LOG_DEBUG, DEBUG_LOG, 0, "testInstrument() end");
}
void	InstrumentTest::testSave() {
	debug(LOG_DEBUG, DEBUG_LOG, 0, "testSave() begin");
	ConfigurationPtr	config = Configuration::get(dbfilename);
	Database	database = config->database();
	DeviceMapperConfigurationPtr	devicemapperconfig
		= DeviceMapperConfiguration::get(config);

	// make sure we have an entry in the device mapper for TEST
	DeviceMapperPtr	devicemapper = devicemapperconfig->devicemapper();
	DeviceMap	mapentry(DeviceName("filterwheel:sx/0"));
	mapentry.name("TEST");
	mapentry.unitid(1291);
	mapentry.description("test filterwheel");
	devicemapper->add(mapentry);

	// create an instrument
	InstrumentPtr	instrument(new Instrument(database, "BLUBB"));

	// add a few components
	InstrumentComponentPtr	camera = InstrumentComponentPtr(
		new InstrumentComponentDirect(DeviceName::Camera,
			DeviceName("camera:simulator/camera"), 7, "localhost"));
	instrument->add(camera);
	InstrumentComponentPtr	ccd = InstrumentComponentPtr(
		new InstrumentComponentDerived(DeviceName::Ccd,
			*instrument, DeviceName::Camera, 5));
	instrument->add(ccd);
	InstrumentComponentPtr	filterwheel = InstrumentComponentPtr(
		new InstrumentComponentMapped(DeviceName::Filterwheel,
			database, "TEST"));
	instrument->add(filterwheel);

	// add the instrument to the database
	InstrumentConfigurationPtr	instrumentconfig
		= InstrumentConfiguration::get(config);
	instrumentconfig->addInstrument(instrument);

	debug(LOG_DEBUG, DEBUG_LOG, 0, "testSave() end");
}
int	main(int argc, char *argv[]) {
	debugthreads = 1;
	Exposure	exposure;
	unsigned int	nImages = 1;
	std::string	reponame;
	std::string	filtername;
	int	c;
	int	longindex;
	double	temperature = std::numeric_limits<double>::quiet_NaN();
	while (EOF != (c = getopt_long(argc, argv, "b:c:de:f:hn:p:r:t:?",
		longopts, &longindex)))
		switch (c) {
		case 'b':
			exposure.mode(Binning(optarg));
			break;
		case 'c':
			Configuration::set_default(optarg);
			break;
		case 'd':
			debuglevel = LOG_DEBUG;
			break;
		case 'e':
			exposure.exposuretime(atof(optarg));
			break;
		case 'f':
			filtername = std::string(optarg);
			break;
		case 'h':
		case '?':
			usage(argv[0]);
			return EXIT_SUCCESS;
		case 'n':
			nImages = atoi(optarg);
			break;
		case 'p':
			exposure.purpose(Exposure::string2purpose(optarg));
			break;
		case 'r':
			reponame = std::string(optarg);
			break;
		case 't':
			temperature = atof(optarg);
			break;
		case 1:
			exposure.frame(ImageRectangle(optarg));
			break;
		default:
			throw std::runtime_error("unknown option");
		}

	// next argument must be instrument name or help
	if (optind >= argc) {
		std::cerr << "missing instrument name" << std::endl;
		return EXIT_FAILURE;
	}
	std::string	instrumentname(argv[optind++]);

	// get the configuration
	ConfigurationPtr	config = Configuration::get();

	// backend for instruments
	InstrumentBackend	instrumentbackend(config->database());
        InstrumentPtr   instrument = instrumentbackend.get(instrumentname);

	// get the image repository
	ImageRepoPtr	repo(NULL);
	if (reponame.size() > 0) {
		ImageRepoConfigurationPtr	imagerepos
			= ImageRepoConfiguration::get(config);
		repo = imagerepos->repo(reponame);
	}

	// prepare a repository from which we can extract the devices
	Repository	repository;
	Devices		devices(repository);

	// get the devices
	CameraPtr	camera = devices.getCamera(instrument->getCamera(0).deviceurl());
	CcdPtr		ccd = devices.getCcd(instrument->getCcd(0).deviceurl());

	// If temperature is set, and a cooler is present, initialize the
	// cooler and wait until temperature is reached
	CoolerPtr	cooler(NULL);
	if ((temperature == temperature)
		&& (instrument->hasCooler())) {
		double	absolute = 273.15 + temperature;
		if (absolute < 0) {
			std::string	msg
				= stringprintf("illegal temperature: %f",
					temperature);
			debug(LOG_ERR, DEBUG_LOG, 0, "%s", msg.c_str());
			throw std::runtime_error(msg);
		}
		cooler = devices.getCooler(instrument->getCooler(0).deviceurl());
		cooler->setTemperature(absolute);
		cooler->setOn(true);

		double	delta;
		do {
			double	actual = cooler->getActualTemperature();
			delta = fabs(absolute - actual);
			debug(LOG_DEBUG, DEBUG_LOG, 0,
				"set: %.1f, actual: %.1f, delta: %.1f",
				absolute, actual, delta);
		} while (delta > 1);
	}

	// if the instrument has a filter wheel, we get a pointer to it
	// and try 
	FilterWheelPtr	filterwheel(NULL);
	if (instrument->hasFilterWheel()) {
		filterwheel = devices.getFilterWheel(
			instrument->getFilterWheel(0).deviceurl());
		filterwheel->wait(20);
		if (filtername.size() > 0) {
			filterwheel->select(filtername);
			filterwheel->wait(20);
		}
	}

	// start the stream
	unsigned int	imagesRetrieved = 0;
	ccd->startStream(exposure);
	while (imagesRetrieved < nImages) {
		ImagePtr	image = ccd->getEntry(true).image;
		debug(LOG_DEBUG, DEBUG_LOG, 0, "got image[%u] %s",
			++imagesRetrieved,
			image->size().toString().c_str());
		if (!image->hasMetadata(std::string("INSTRUME"))) {
			image->setMetadata(FITSKeywords::meta(
				std::string("INSTRUME"), instrument->name()));
		}
		// do something about the image
		if (repo) {
			repo->save(image);
		}
	}
	// stop the stream
	ccd->stopStream();

	// find out how many images were dropped
	if (ccd->dropped() > 0) {
		std::cerr << "images dropped: " << ccd->dropped() << std::endl;
	}

	// turn off the cooler
	if (cooler) {
		cooler->setOn(false);
	}
	
	return EXIT_SUCCESS;
}
/**
 * \brief Main function for the snowfocus program
 */
int	main(int argc, char *argv[]) {
	snowstar::CommunicatorSingleton	cs(argc, argv);

	std::string	instrumentname;
	int	steps = 10;
	double	exposuretime = 1.0;
	double	temperature = std::numeric_limits<double>::quiet_NaN();
	std::string	binning;
	std::string	frame;
	std::string	filtername;
	astro::focusing::Focusing::method_type	method
		= astro::focusing::Focusing::FWHM;

	int	c;
	int	longindex;
	while (EOF != (c = getopt_long(argc, argv, "b:c:de:f:hi:m:r:t:",
		longopts, &longindex)))
		switch (c) {
		case 'b':
			binning = optarg;
			break;
		case 'c':
			astro::config::Configuration::set_default(optarg);
			break;
		case 'd':
			debuglevel = LOG_DEBUG;
			break;
		case 'e':
			exposuretime = std::stod(optarg);
			break;
		case 'f':
			filtername = optarg;
			break;
		case 'h':
			usage(argv[0]);
			return EXIT_SUCCESS;
		case 'i':
			instrumentname = optarg;
			break;
		case 'm':
			method = astro::focusing::Focusing::string2method(optarg);
			break;
		case 'r':
			frame = optarg;
			break;
		case 's':
			steps = std::stoi(optarg);
			break;
		case 't':
			temperature = std::stod(optarg);
			break;
		}

	// the next argument is the command
	if (argc <= optind) {
		throw std::runtime_error("not enough arguments");
	}
	std::string	command = argv[optind++];
	debug(LOG_DEBUG, DEBUG_LOG, 0, "command: %s", command.c_str()); 
	if (command == "help") {
		usage(argv[0]);
		return EXIT_SUCCESS;
	}


	// get the configuration
	ConfigurationPtr	config = Configuration::get();

	// check whether we have an instrument
	if (0 == instrumentname.size()) {
		throw std::runtime_error("instrument name not set");
	}
	RemoteInstrument	instrument(config->database(),
						instrumentname);
	// get the device names
	CcdPrx	ccdprx = instrument.ccd_proxy();
	std::string	ccdname = ccdprx->getName();
	FocuserPrx	focuserprx = instrument.focuser_proxy();
	std::string	focusername = focuserprx->getName();
	debug(LOG_DEBUG, DEBUG_LOG, 0, "ccd: %s focuser: %s", ccdname.c_str(),
		focusername.c_str());

	// first get a connection to the server
	Ice::CommunicatorPtr	ic = CommunicatorSingleton::get();
	astro::ServerName	servername
		= instrument.servername(astro::DeviceName::Ccd);
	Ice::ObjectPrx	base = ic->stringToProxy(
				servername.connect("FocusingFactory"));
	FocusingFactoryPrx	focusingfactory
		= FocusingFactoryPrx::checkedCast(base);

	// get the focusing interface
	FocusingPrx	focusing = focusingfactory->get(ccdname, focusername);
	debug(LOG_DEBUG, DEBUG_LOG, 0, "got a focusing proxy");

	// creating a callback
	Ice::ObjectPtr	callback = new FocusCallbackI();
	CallbackAdapter	adapter(ic);
	Ice::Identity	ident = adapter.add(callback);
	focusing->ice_getConnection()->setAdapter(adapter.adapter());

	// handle the simple commands
	if (command == "status") {
		std::cout << "status: ";
		std::cout << focusingstate2string(focusing->status());
		std::cout << std::endl;
		return EXIT_SUCCESS;
	}
	if (command == "history") {
		show_history(focusing->history());
		return EXIT_SUCCESS;
	}
	if (command == "monitor") {
		std::cout << "current status: ";
		std::cout << focusingstate2string(focusing->status());
		std::cout << std::endl;
		focusing->registerCallback(ident);
		signal(SIGINT, handler);
		while (!signal_received) {
			sleep(1);
		}
		focusing->unregisterCallback(ident);
		return EXIT_SUCCESS;
	}

	if (command == "cancel") {
		focusing->cancel();
		std::cout << "cancel command sent" << std::endl;
		return EXIT_SUCCESS;
	}

	// throw exception for unknown commands
	if (command != "start") {
		throw std::runtime_error("unknown command");
	}

	// make sure temperature is set
	CoolerPrx	cooler;
	if (instrument.has(DeviceName::Cooler)) {
		cooler = instrument.cooler_proxy();
	}
	CoolerTask      coolertask(cooler, temperature);
	coolertask.wait();

	// next two arguments are the interval boundaries
	if ((argc - optind) < 2) {
		throw std::runtime_error("missing intervale arguments");
	}
	int	min = std::stoi(argv[optind++]);
	int	max = std::stoi(argv[optind++]);
	debug(LOG_DEBUG, DEBUG_LOG, 0, "interval [%d,%d]", min, max);
	if (min >= max) {
		throw std::runtime_error("not an interval");
	}


	// ensure that focuser is ready
	FocusState	state = focusing->status();
	debug(LOG_DEBUG, DEBUG_LOG, 0, "current state = %d", state);
	if ((state == FocusMOVING) && (state == FocusMEASURING)) {
		throw std::runtime_error("already focusing");
	}

	// set up the exposure
	CcdTask	ccdtask(ccdprx);
	ccdtask.frame(frame);
	ccdtask.binning(binning);
	ccdtask.exposuretime(exposuretime);

	// set up the focusing
	focusing->setSteps(steps);
	focusing->setMethod(convert(method));

	// start the focusing process
	debug(LOG_DEBUG, DEBUG_LOG, 0, "starting between %d and %d", min, max);
	focusing->start(min, max);
	debug(LOG_DEBUG, DEBUG_LOG, 0, "focusing started, status: %d",
		focusing->status());

	// wait for the process to complete
	bool	completed = false;
	signal(SIGINT, handler);
	do {
		sleep(1);
		switch (focusing->status()) {
        	case FocusIDLE:
        	case FocusMOVING:
        	case FocusMEASURING:
			break;
        	case FocusFOCUSED:
        	case FocusFAILED:
			completed = true;
			break;
		}
	} while ((!completed) && (!signal_received));
	if (completed) {
		std::cout << "final focus position: " << focuserprx->current();
		std::cout << std::endl;

		// display the history
		show_history(focusing->history());
	} else {
		std::cout << "focusing incomplete" << std::endl;
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}
/**
 * \brief Get the device mapper
 */
DeviceMapperPtr	DeviceMapperConfigurationBackend::devicemapper() {
    return DeviceMapper::get(_config->database());
}