<commit_before>#pragma once
#define BOOST_PP_VARIADICS 1
#include <boost/preprocessor.hpp>
#include <boost/format.hpp>
#include <iostream>
#include <sstream>
#include "argholder.hpp"

#ifdef ANDROID
	#include <android/log.h>
	#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__))
	#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "threaded_app", __VA_ARGS__))
	/* For debug builds, always enable the debug traces in this library */
	#ifndef NDEBUG
	#  define LOGV(...)  ((void)__android_log_print(ANDROID_LOG_VERBOSE, "threaded_app", __VA_ARGS__))
	#else
	#  define LOGV(...)  ((void)0)
	#endif
#endif

// Debug=assert, Release=throw		[Assert_Trap]
// Debug=assert, Release=none		[Assert_TrapP]
// Debug=throw, Release=throw		[Assert_Throw]
// Debug=throw, Release=none		[Assert_ThrowP]
// Debug=warning, Release=warning	[Assert_Warn]
// Debug=warning, Release=none		[Assert_WarnP]

#define _Assert_Base(expr, act, ...)			{if(!(expr)) { act.onError(MakeAssertMsg(#expr, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)); }}
#define _AssertT(act, expr, throwtype)			_Assert_Base(expr, (AAct_##act<BOOST_PP_SEQ_ENUM(throwtype)>()), BOOST_PP_STRINGIZE(BOOST_PP_SEQ_ELEM(0,throwtype)))
#define _AssertTArg(act, expr, throwtype, ...)	_Assert_Base(expr, (AAct_##act<BOOST_PP_SEQ_ENUM(throwtype)>(__VA_ARGS__)), BOOST_PP_STRINGIZE(BOOST_PP_SEQ_ELEM(0,throwtype)))
#define AssertT(act, expr, ...)					BOOST_PP_IF(BOOST_PP_EQUAL(1, BOOST_PP_VARIADIC_SIZE(__VA_ARGS__)), _AssertT, _AssertTArg)(act,expr,__VA_ARGS__)

#define _Assert(act, expr)						_AssertArg(act, expr, "Assertion failed")
#define _AssertArg(act, expr, ...)				_Assert_Base(expr, (AAct_##act<std::runtime_error>()), __VA_ARGS__)
#define Assert(act, ...)						BOOST_PP_IF(BOOST_PP_EQUAL(1, BOOST_PP_VARIADIC_SIZE(__VA_ARGS__)), _Assert, _AssertArg)(act, __VA_ARGS__)

#ifdef DEBUG
	#define AssertP(act, ...)						Assert(act, __VA_ARGS__)
	#define AssertTP(act, expr, throwtype, ...)		AssertT(act,expr,throwtype, __VA_ARGS__)
#else
	#define AssertP(act, ...)
	#define AssertTP(act, expr, throwtype, ...)
#endif

template <class... Ts>
std::string ConcatMessage(boost::format& fmt, Ts&&... t) { return fmt.str(); }
template <class T, class... Ts>
std::string ConcatMessage(boost::format& fmt, T&& t, Ts&&... ts) {
	fmt % std::forward<T>(t);
	return ConcatMessage(fmt, std::forward<Ts>(ts)...);
}
template <class... Ts>
std::string MakeAssertMsg(const char* base, const char* filename, const char* funcname, int line, const char* fmt, Ts&&... ts) {
	using std::endl;
	boost::format bf(fmt);
	std::stringstream ss;
	ss << ConcatMessage(bf, std::forward<Ts>(ts)...) << endl
		<< base << endl
		<< "at file:\t" << filename << endl
		<< "at function:\t" << funcname << endl
		<< "on line:\t" << line << endl;
	return ss.str();
}

using LogOUT = std::function<void (const std::string&)>;
extern LogOUT g_logOut;
void LogOutput(const std::string& s);
template <class... Ts>
void LogOutput(const char* fmt, Ts&&... ts) {
	boost::format msg(fmt);
	LogOutput(ConcatMessage(msg, std::forward<Ts>(ts)...));
}

//! エラー時にメッセージ出力だけする
template <class E, class... Ts>
struct AAct_Warn {
	template <class... TA>
	AAct_Warn(TA&&...) {}
	void onError(const std::string& str) {
		LogOutput(str);
	}
};
//! エラー時にメッセージ出力と例外の送出を行う
template <class E, class... Ts>
struct AAct_Throw : spn::ArgHolder<Ts...> {
	using spn::ArgHolder<Ts...>::ArgHolder;
	static void _Throw(const std::string& str) { throw E(str); }
	template <class T2, class... Ts2>
	static void _Throw(const std::string& str, T2&& t, Ts2&&... ts) {
		AAct_Warn<E>().onError(str);
		throw E(std::forward<T2>(t), std::forward<Ts2>(ts)...); }
	void onError(const std::string& str) {
		this->inorder([&str](Ts... args){
			_Throw(str, args...);
		});
	}
};
//! エラー時にデバッガをブレーク、リリース時には例外の送出を行う
template <class E, class... Ts>
struct AAct_Trap : AAct_Throw<E,Ts...> {
	using base_type = AAct_Throw<E,Ts...>;
	using base_type::base_type;
	void onError(const std::string& str) {
		#ifdef DEBUG
			AAct_Warn<E>().onError(str);
			__builtin_trap();
		#else
			base_type::onError(str);
		#endif
	}
};
namespace spn {
	template <class Act, class Chk, class... Ts>
	void CheckError(Act&& act, Chk&& chk, const char* filename, const char* funcname, int line, Ts&&... ts) {
		const char* msg = chk.errorDesc(std::forward<Ts>(ts)...);
		if(msg)
			act.onError(MakeAssertMsg(chk.getAPIName(), filename, funcname, line, msg));
	}
	template <class Act, class Chk>
	struct ErrorChecker {
		Act			_act;
		Chk			_chk;
		int			_line;
		const char	*_filename, *_fname;
		ErrorChecker(Act&& act, Chk&& chk, const char* filename, const char* fname, int line): _act(std::move(act)), _chk(std::move(chk)), _line(line), _filename(filename), _fname(fname) {
			chk.reset();
		}
		~ErrorChecker() {
			CheckError(_act, _chk, _filename, _fname, _line);
		}
	};
	template <class Act, class Chk, class Func, class... TsA>
	auto EChk_base(Act&& act, Chk&& chk, const char* filename, const char* fname, int line, const Func& func, TsA&&... ts) -> decltype(func(std::forward<TsA>(ts)...)) {
		ErrorChecker<typename std::decay<Act>::type, typename std::decay<Chk>::type> chker(std::forward<Act>(act), std::forward<Chk>(chk), filename, fname, line);
		return func(std::forward<TsA>(ts)...);
	}
	template <class Act, class Chk>
	void EChk_base(Act&& act, Chk&& chk, const char* filename, const char* fname, int line) {
		ErrorChecker<typename std::decay<Act>::type, typename std::decay<Chk>::type> chker(std::forward<Act>(act), std::forward<Chk>(chk), filename, fname, line);
	}
	template <class Func, class... TsA>
	auto EChk_pass(const Func& func, TsA&&... ts) -> decltype(func(std::forward<TsA>(ts)...)) {
		return func(std::forward<TsA>(ts)...);
	}
	template <class Act, class Chk, class Func, class... TsA>
	auto EChk_baseA1(Act&& act, Chk&& chk, const char* filename, const char* fname, int line, const Func& func, TsA&&... ts) -> decltype(func(std::forward<TsA>(ts)...)) {
		auto val = func(std::forward<TsA>(ts)...);
		CheckError(std::forward<Act>(act), std::forward<Chk>(chk), filename, fname, line, val);
		return val;
	}
	template <class Act, class Chk, class RES>
	RES EChk_baseA2(Act&& act, Chk&& chk, const char* filename, const char* fname, int line, const RES& res) {
		CheckError(std::forward<Act>(act), std::forward<Chk>(chk), filename, fname, line, res);
		return res;
	}
}
<commit_msg>AssertTマクロの修正 引数に関するエラー<commit_after>#pragma once
#define BOOST_PP_VARIADICS 1
#include <boost/preprocessor.hpp>
#include <boost/format.hpp>
#include <iostream>
#include <sstream>
#include "argholder.hpp"

#ifdef ANDROID
	#include <android/log.h>
	#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__))
	#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "threaded_app", __VA_ARGS__))
	/* For debug builds, always enable the debug traces in this library */
	#ifndef NDEBUG
	#  define LOGV(...)  ((void)__android_log_print(ANDROID_LOG_VERBOSE, "threaded_app", __VA_ARGS__))
	#else
	#  define LOGV(...)  ((void)0)
	#endif
#endif

// Debug=assert, Release=throw		[Assert_Trap]
// Debug=assert, Release=none		[Assert_TrapP]
// Debug=throw, Release=throw		[Assert_Throw]
// Debug=throw, Release=none		[Assert_ThrowP]
// Debug=warning, Release=warning	[Assert_Warn]
// Debug=warning, Release=none		[Assert_WarnP]

#define _Assert_Base(expr, act, ...)			{if(!(expr)) { act.onError(MakeAssertMsg(#expr, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)); }}
#define _AssertT(act, expr, throwtype)			_Assert_Base(expr, (AAct_##act<BOOST_PP_SEQ_ENUM(throwtype)>()), BOOST_PP_STRINGIZE(BOOST_PP_SEQ_ELEM(0,throwtype)))
#define _AssertTArg(act, expr, throwtype, ...)	_Assert_Base(expr, (AAct_##act<BOOST_PP_SEQ_ENUM(throwtype)>(__VA_ARGS__)), BOOST_PP_STRINGIZE(BOOST_PP_SEQ_ELEM(0,throwtype)))
// 任意の例外クラスを指定してアサートチェック
// 引数の有無で_AssertT(), _AssertTArg()を呼び分ける
#define AssertT(act, expr, ...)					BOOST_PP_IF(BOOST_PP_EQUAL(1, BOOST_PP_VARIADIC_SIZE(__VA_ARGS__)), _AssertT, _AssertTArg)(act,expr,__VA_ARGS__)
// デフォルトのエラーメッセージでアサートチェック
#define _Assert(act, expr)						_AssertArg(act, expr, "Assertion failed")
#define _AssertArg(act, expr, ...)				_Assert_Base(expr, (AAct_##act<std::runtime_error>()), __VA_ARGS__)
// Act,Exprに続く引数が無いときは_Assert(), あるなら_AssertArg()を呼ぶ
// 例外形式はstd::runtime_errorとして与えられた引数でエラーメッセージを生成
#define Assert(act, ...)						BOOST_PP_IF(BOOST_PP_EQUAL(1, BOOST_PP_VARIADIC_SIZE(__VA_ARGS__)), _Assert, _AssertArg)(act, __VA_ARGS__)

#ifdef DEBUG
	#define AssertP(act, ...)						Assert(act, __VA_ARGS__)
	#define AssertTP(act, expr, throwtype, ...)		AssertT(act,expr,throwtype, __VA_ARGS__)
#else
	#define AssertP(act, ...)
	#define AssertTP(act, expr, throwtype, ...)
#endif

template <class... Ts>
std::string ConcatMessage(boost::format& fmt, Ts&&... t) { return fmt.str(); }
template <class T, class... Ts>
std::string ConcatMessage(boost::format& fmt, T&& t, Ts&&... ts) {
	fmt % std::forward<T>(t);
	return ConcatMessage(fmt, std::forward<Ts>(ts)...);
}
template <class... Ts>
std::string MakeAssertMsg(const char* base, const char* filename, const char* funcname, int line, const char* fmt, Ts&&... ts) {
	using std::endl;
	boost::format bf(fmt);
	std::stringstream ss;
	ss << ConcatMessage(bf, std::forward<Ts>(ts)...) << endl
		<< base << endl
		<< "at file:\t" << filename << endl
		<< "at function:\t" << funcname << endl
		<< "on line:\t" << line << endl;
	return ss.str();
}

using LogOUT = std::function<void (const std::string&)>;
extern LogOUT g_logOut;
void LogOutput(const std::string& s);
template <class... Ts>
void LogOutput(const char* fmt, Ts&&... ts) {
	boost::format msg(fmt);
	LogOutput(ConcatMessage(msg, std::forward<Ts>(ts)...));
}

//! エラー時にメッセージ出力だけする
template <class E, class... Ts>
struct AAct_Warn {
	template <class... TA>
	AAct_Warn(TA&&...) {}
	void onError(const std::string& str) {
		LogOutput(str);
	}
};
//! エラー時にメッセージ出力と例外の送出を行う
template <class E, class... Ts>
struct AAct_Throw : spn::ArgHolder<Ts...> {
	using spn::ArgHolder<Ts...>::ArgHolder;
	// 例外クラスがconst std::stringでコンストラクトできる場合はエラーメッセージを渡し、残りの引数を無視
	template <class E2, class... TArg, class Dummy=void*,
		class=typename std::enable_if<
			std::is_constructible<E2, const std::string&>::value
			>::type
		>
	static void __Throw(const std::string& str, TArg... args) {
		throw E(str); }

	// そうでない場合はエラーメッセージをその場で出力し、引数はユーザーが渡したものを指定
	template <class E2, class... TArg,
		class=typename std::enable_if<
			!std::is_constructible<E2, const std::string&>::value
			>::type
		>
	static void __Throw(const std::string& str, TArg... args) {
		AAct_Warn<E2>().onError(str);
		throw E(args...); }

	template <class T2, class... Ts2>
	static void _Throw(const std::string& str, T2&& t, Ts2&&... ts) {
		AAct_Warn<E>().onError(str);
		throw E(std::forward<T2>(t), std::forward<Ts2>(ts)...); }
	void onError(const std::string& str) {
		this->inorder([&str](Ts... args){
			__Throw<E>(str, args...);
		});
	}
};
//! エラー時にデバッガをブレーク、リリース時には例外の送出を行う
template <class E, class... Ts>
struct AAct_Trap : AAct_Throw<E,Ts...> {
	using base_type = AAct_Throw<E,Ts...>;
	using base_type::base_type;
	void onError(const std::string& str) {
		#ifdef DEBUG
			AAct_Warn<E>().onError(str);
			__builtin_trap();
		#endif
		base_type::onError(str);
	}
};
namespace spn {
	template <class Act, class Chk, class... Ts>
	void CheckError(Act&& act, Chk&& chk, const char* filename, const char* funcname, int line, Ts&&... ts) {
		const char* msg = chk.errorDesc(std::forward<Ts>(ts)...);
		if(msg)
			act.onError(MakeAssertMsg(chk.getAPIName(), filename, funcname, line, msg));
	}
	template <class Act, class Chk>
	struct ErrorChecker {
		Act			_act;
		Chk			_chk;
		int			_line;
		const char	*_filename, *_fname;
		ErrorChecker(Act&& act, Chk&& chk, const char* filename, const char* fname, int line): _act(std::move(act)), _chk(std::move(chk)), _line(line), _filename(filename), _fname(fname) {
			chk.reset();
		}
		~ErrorChecker() {
			CheckError(_act, _chk, _filename, _fname, _line);
		}
	};
	template <class Act, class Chk, class Func, class... TsA>
	auto EChk_base(Act&& act, Chk&& chk, const char* filename, const char* fname, int line, const Func& func, TsA&&... ts) -> decltype(func(std::forward<TsA>(ts)...)) {
		ErrorChecker<typename std::decay<Act>::type, typename std::decay<Chk>::type> chker(std::forward<Act>(act), std::forward<Chk>(chk), filename, fname, line);
		return func(std::forward<TsA>(ts)...);
	}
	template <class Act, class Chk>
	void EChk_base(Act&& act, Chk&& chk, const char* filename, const char* fname, int line) {
		ErrorChecker<typename std::decay<Act>::type, typename std::decay<Chk>::type> chker(std::forward<Act>(act), std::forward<Chk>(chk), filename, fname, line);
	}
	template <class Func, class... TsA>
	auto EChk_pass(const Func& func, TsA&&... ts) -> decltype(func(std::forward<TsA>(ts)...)) {
		return func(std::forward<TsA>(ts)...);
	}
	template <class Act, class Chk, class Func, class... TsA>
	auto EChk_baseA1(Act&& act, Chk&& chk, const char* filename, const char* fname, int line, const Func& func, TsA&&... ts) -> decltype(func(std::forward<TsA>(ts)...)) {
		auto val = func(std::forward<TsA>(ts)...);
		CheckError(std::forward<Act>(act), std::forward<Chk>(chk), filename, fname, line, val);
		return val;
	}
	template <class Act, class Chk, class RES>
	RES EChk_baseA2(Act&& act, Chk&& chk, const char* filename, const char* fname, int line, const RES& res) {
		CheckError(std::forward<Act>(act), std::forward<Chk>(chk), filename, fname, line, res);
		return res;
	}
}
<|endoftext|>


<commit_before>/*
 Copyright (c) 2014, The Cinder Project, All rights reserved.

 This code is intended for use with the Cinder C++ library: http://libcinder.org

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

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

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

#include "cinder/Log.h"
#include "cinder/CinderAssert.h"
#include "cinder/Utilities.h"
#include "cinder/app/App.h"

#if defined( CINDER_COCOA )
	#import <Foundation/Foundation.h>
#endif

#if defined( CINDER_COCOA ) && ( ! defined( __OBJC__ ) )
	#error "This file must be compiled as Objective-C++ on the Mac"
#endif

#include <mutex>
#include <time.h>

#define DEFAULT_FILE_LOG_PATH "cinder.log"

// TODO: consider storing Logger's as shared_ptr instead
//	- they really aren't shared, but makes swapping them in and out and LogManager's handles easier

using namespace std;

namespace cinder { namespace log {

class LoggerImplMulti : public Logger {
public:

	void add( Logger *logger )							{ mLoggers.push_back( std::unique_ptr<Logger>( logger ) ); }
	void add( std::unique_ptr<Logger> &&logger )		{ mLoggers.emplace_back( move( logger ) ); }

	template <typename LoggerT>
	Logger* findType();

	void remove( Logger *logger );

	const vector<unique_ptr<Logger> >& getLoggers() const	{ return mLoggers; }

	virtual void write( const Metadata &meta, const std::string &text ) override;

private:
	vector<unique_ptr<Logger> >	mLoggers; // TODO: make set? don't want duplicates
};

namespace  {

// output format is YYYY-MM-DD.HH:mm:ss
const std::string getCurrentDateTimeString()
{
	time_t timeSinceEpoch = time( NULL );
	struct tm *now = localtime( &timeSinceEpoch );

	char result[100];
	strftime( result, sizeof( result ), "%Y-%m-%d.%X", now );

	return result;
}

} // anonymous namespace

// ----------------------------------------------------------------------------------------------------
// MARK: - LogManager
// ----------------------------------------------------------------------------------------------------

LogManager*	LogManager::sInstance = new LogManager;	// note: leaks to enable logging during shutdown

LogManager* manager()
{
	return LogManager::instance();
}

LogManager::LogManager()
{
	restoreToDefault();
}

void LogManager::resetLogger( Logger *logger )
{
	lock_guard<mutex> lock( mMutex );

	mLogger.reset( logger );

	LoggerImplMulti *multi = dynamic_cast<LoggerImplMulti *>( logger );
	mLoggerMulti = multi ? multi : nullptr;

	mConsoleLoggingEnabled = mFileLoggingEnabled = mSystemLoggingEnabled = false;
}

void LogManager::addLogger( Logger *logger )
{
	lock_guard<mutex> lock( mMutex );

	if( ! mLoggerMulti ) {
		auto loggerMulti = unique_ptr<LoggerImplMulti>( new LoggerImplMulti );
		loggerMulti->add( move( mLogger ) );
		mLoggerMulti = loggerMulti.get();
		mLogger = move( loggerMulti );
	}

	mLoggerMulti->add( logger );
}

void LogManager::removeLogger( Logger *logger )
{
	CI_ASSERT( mLoggerMulti );

	mLoggerMulti->remove( logger );
}

void LogManager::restoreToDefault()
{
	lock_guard<mutex> lock( mMutex );

	mLogger.reset( new LoggerConsoleThreadSafe );
	mLoggerMulti = nullptr;
	mConsoleLoggingEnabled = true;
	mFileLoggingEnabled = false;
	mSystemLoggingEnabled = false;
}

vector<Logger *> LogManager::getAllLoggers()
{
	vector<Logger *> result;

	if( mLoggerMulti ) {
		for( const auto &logger : mLoggerMulti->getLoggers() )
			result.push_back( logger.get() );
	}
	else
		result.push_back( mLogger.get() );

	return result;
}

void LogManager::enableConsoleLogging()
{
	if( mConsoleLoggingEnabled )
		return;

	addLogger( new LoggerConsoleThreadSafe );
	mConsoleLoggingEnabled = true;
}

void LogManager::enableFileLogging( const fs::path &path )
{
	if( mFileLoggingEnabled ) {
		// if path has changed, destroy previous file logger and make a new one
		auto logger = mLoggerMulti->findType<LoggerFile>();
		if( logger )
			disableFileLogging();
		else
			return;
	}

	addLogger( new LoggerFileThreadSafe( path ) );
	mFileLoggingEnabled = true;
}

void LogManager::enableSystemLogging()
{
	if( mSystemLoggingEnabled )
		return;

#if defined( CINDER_COCOA )
	addLogger( new LoggerNSLog );
	mSystemLoggingEnabled = true;
#endif
}

void LogManager::disableConsoleLogging()
{
	if( ! mConsoleLoggingEnabled || ! mLoggerMulti )
		return;

	auto logger = mLoggerMulti->findType<LoggerConsole>();
	mLoggerMulti->remove( logger );

	mConsoleLoggingEnabled = false;
}

void LogManager::disableFileLogging()
{
	if( ! mFileLoggingEnabled || ! mLoggerMulti )
		return;

	auto logger = mLoggerMulti->findType<LoggerFile>();
	mLoggerMulti->remove( logger );

	mFileLoggingEnabled = false;
}

void LogManager::disableSystemLogging()
{
	if( ! mSystemLoggingEnabled || ! mLoggerMulti )
		return;

#if defined( CINDER_COCOA )
	auto logger = mLoggerMulti->findType<LoggerNSLog>();
	mLoggerMulti->remove( logger );
	mSystemLoggingEnabled = false;
#endif
}

// ----------------------------------------------------------------------------------------------------
// MARK: - Logger
// ----------------------------------------------------------------------------------------------------

void Logger::writeDefault( std::ostream &stream, const Metadata &meta, const std::string &text )
{
	stream << meta.mLevel << " ";

	if( isTimestampEnabled() )
		stream << getCurrentDateTimeString() << " ";

	stream << meta.mLocation << " " << text << endl;
}

// ----------------------------------------------------------------------------------------------------
// MARK: - LoggerConsole
// ----------------------------------------------------------------------------------------------------

void LoggerConsole::write( const Metadata &meta, const string &text )
{
	writeDefault( app::console(), meta, text );
}

// ----------------------------------------------------------------------------------------------------
// MARK: - LoggerImplMulti
// ----------------------------------------------------------------------------------------------------

template <typename LoggerT>
Logger* LoggerImplMulti::findType()
{
	for( const auto &logger : mLoggers ) {
		if( dynamic_cast<LoggerT *>( logger.get() ) )
			return logger.get();
	}

	return nullptr;
}

void LoggerImplMulti::remove( Logger *logger )
{
	mLoggers.erase( remove_if( mLoggers.begin(), mLoggers.end(),
							  [logger]( const std::unique_ptr<Logger> &o ) {
								  return o.get() == logger;
							  } ),
				   mLoggers.end() );
}

void LoggerImplMulti::write( const Metadata &meta, const string &text )
{
	for( auto &logger : mLoggers )
		logger->write( meta, text );
}

// ----------------------------------------------------------------------------------------------------
// MARK: - LoggerFile
// ----------------------------------------------------------------------------------------------------

LoggerFile::LoggerFile( const fs::path &filePath )
	: mFilePath( filePath )
{
	if( mFilePath.empty() )
		mFilePath = DEFAULT_FILE_LOG_PATH;

	setTimestampEnabled();

	mStream.open( mFilePath.string() );
}

LoggerFile::~LoggerFile()
{
	mStream.close();
}

void LoggerFile::write( const Metadata &meta, const string &text )
{
	writeDefault( mStream, meta, text );
}

#if defined( CINDER_COCOA )

// ----------------------------------------------------------------------------------------------------
// MARK: - LoggerNSLog
// ----------------------------------------------------------------------------------------------------

void LoggerNSLog::write( const Metadata &meta, const string &text )
{
	NSString *textNs = [NSString stringWithCString:text.c_str() encoding:NSUTF8StringEncoding];
	NSString *metaDataNs = [NSString stringWithCString:meta.toString().c_str() encoding:NSUTF8StringEncoding];
	NSLog( @"%@%@", metaDataNs, textNs );
}

#endif

// ----------------------------------------------------------------------------------------------------
// MARK: - Helper Classes
// ----------------------------------------------------------------------------------------------------

string Metadata::toString() const
{
	stringstream ss;
	ss << mLevel << " " << mLocation;
	return ss.str();
}

ostream& operator<<( ostream &os, const Location &rhs )
{
	os << rhs.getFunctionName() << "[" << rhs.getLineNumber() << "]";
	return os;
}

ostream& operator<<( ostream &lhs, const Level &rhs )
{
	switch( rhs ) {
		case LEVEL_VERBOSE:		lhs << "|verbose|";	break;
		case LEVEL_INFO:		lhs << "|info   |";	break;
		case LEVEL_WARNING:		lhs << "|warning|";	break;
		case LEVEL_ERROR:		lhs << "|error  |";	break;
		case LEVEL_FATAL:		lhs << "|fatal  |";	break;
		default: CI_ASSERT_NOT_REACHABLE();
	}
	return lhs;
}

} } // namespace cinder::log
<commit_msg>LoggerFile: Create the file only if/when needed.<commit_after>/*
 Copyright (c) 2014, The Cinder Project, All rights reserved.

 This code is intended for use with the Cinder C++ library: http://libcinder.org

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

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

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

#include "cinder/Log.h"
#include "cinder/CinderAssert.h"
#include "cinder/Utilities.h"
#include "cinder/app/App.h"

#if defined( CINDER_COCOA )
	#import <Foundation/Foundation.h>
#endif

#if defined( CINDER_COCOA ) && ( ! defined( __OBJC__ ) )
	#error "This file must be compiled as Objective-C++ on the Mac"
#endif

#include <mutex>
#include <time.h>

#define DEFAULT_FILE_LOG_PATH "cinder.log"

// TODO: consider storing Logger's as shared_ptr instead
//	- they really aren't shared, but makes swapping them in and out and LogManager's handles easier

using namespace std;

namespace cinder { namespace log {

class LoggerImplMulti : public Logger {
public:

	void add( Logger *logger )							{ mLoggers.push_back( std::unique_ptr<Logger>( logger ) ); }
	void add( std::unique_ptr<Logger> &&logger )		{ mLoggers.emplace_back( move( logger ) ); }

	template <typename LoggerT>
	Logger* findType();

	void remove( Logger *logger );

	const vector<unique_ptr<Logger> >& getLoggers() const	{ return mLoggers; }

	virtual void write( const Metadata &meta, const std::string &text ) override;

private:
	vector<unique_ptr<Logger> >	mLoggers; // TODO: make set? don't want duplicates
};

namespace  {

// output format is YYYY-MM-DD.HH:mm:ss
const std::string getCurrentDateTimeString()
{
	time_t timeSinceEpoch = time( NULL );
	struct tm *now = localtime( &timeSinceEpoch );

	char result[100];
	strftime( result, sizeof( result ), "%Y-%m-%d.%X", now );

	return result;
}

} // anonymous namespace

// ----------------------------------------------------------------------------------------------------
// MARK: - LogManager
// ----------------------------------------------------------------------------------------------------

LogManager*	LogManager::sInstance = new LogManager;	// note: leaks to enable logging during shutdown

LogManager* manager()
{
	return LogManager::instance();
}

LogManager::LogManager()
{
	restoreToDefault();
}

void LogManager::resetLogger( Logger *logger )
{
	lock_guard<mutex> lock( mMutex );

	mLogger.reset( logger );

	LoggerImplMulti *multi = dynamic_cast<LoggerImplMulti *>( logger );
	mLoggerMulti = multi ? multi : nullptr;

	mConsoleLoggingEnabled = mFileLoggingEnabled = mSystemLoggingEnabled = false;
}

void LogManager::addLogger( Logger *logger )
{
	lock_guard<mutex> lock( mMutex );

	if( ! mLoggerMulti ) {
		auto loggerMulti = unique_ptr<LoggerImplMulti>( new LoggerImplMulti );
		loggerMulti->add( move( mLogger ) );
		mLoggerMulti = loggerMulti.get();
		mLogger = move( loggerMulti );
	}

	mLoggerMulti->add( logger );
}

void LogManager::removeLogger( Logger *logger )
{
	CI_ASSERT( mLoggerMulti );

	mLoggerMulti->remove( logger );
}

void LogManager::restoreToDefault()
{
	lock_guard<mutex> lock( mMutex );

	mLogger.reset( new LoggerConsoleThreadSafe );
	mLoggerMulti = nullptr;
	mConsoleLoggingEnabled = true;
	mFileLoggingEnabled = false;
	mSystemLoggingEnabled = false;
}

vector<Logger *> LogManager::getAllLoggers()
{
	vector<Logger *> result;

	if( mLoggerMulti ) {
		for( const auto &logger : mLoggerMulti->getLoggers() )
			result.push_back( logger.get() );
	}
	else
		result.push_back( mLogger.get() );

	return result;
}

void LogManager::enableConsoleLogging()
{
	if( mConsoleLoggingEnabled )
		return;

	addLogger( new LoggerConsoleThreadSafe );
	mConsoleLoggingEnabled = true;
}

void LogManager::enableFileLogging( const fs::path &path )
{
	if( mFileLoggingEnabled ) {
		// if path has changed, destroy previous file logger and make a new one
		auto logger = mLoggerMulti->findType<LoggerFile>();
		if( logger )
			disableFileLogging();
		else
			return;
	}

	addLogger( new LoggerFileThreadSafe( path ) );
	mFileLoggingEnabled = true;
}

void LogManager::enableSystemLogging()
{
	if( mSystemLoggingEnabled )
		return;

#if defined( CINDER_COCOA )
	addLogger( new LoggerNSLog );
	mSystemLoggingEnabled = true;
#endif
}

void LogManager::disableConsoleLogging()
{
	if( ! mConsoleLoggingEnabled || ! mLoggerMulti )
		return;

	auto logger = mLoggerMulti->findType<LoggerConsole>();
	mLoggerMulti->remove( logger );

	mConsoleLoggingEnabled = false;
}

void LogManager::disableFileLogging()
{
	if( ! mFileLoggingEnabled || ! mLoggerMulti )
		return;

	auto logger = mLoggerMulti->findType<LoggerFile>();
	mLoggerMulti->remove( logger );

	mFileLoggingEnabled = false;
}

void LogManager::disableSystemLogging()
{
	if( ! mSystemLoggingEnabled || ! mLoggerMulti )
		return;

#if defined( CINDER_COCOA )
	auto logger = mLoggerMulti->findType<LoggerNSLog>();
	mLoggerMulti->remove( logger );
	mSystemLoggingEnabled = false;
#endif
}

// ----------------------------------------------------------------------------------------------------
// MARK: - Logger
// ----------------------------------------------------------------------------------------------------

void Logger::writeDefault( std::ostream &stream, const Metadata &meta, const std::string &text )
{
	stream << meta.mLevel << " ";

	if( isTimestampEnabled() )
		stream << getCurrentDateTimeString() << " ";

	stream << meta.mLocation << " " << text << endl;
}

// ----------------------------------------------------------------------------------------------------
// MARK: - LoggerConsole
// ----------------------------------------------------------------------------------------------------

void LoggerConsole::write( const Metadata &meta, const string &text )
{
	writeDefault( app::console(), meta, text );
}

// ----------------------------------------------------------------------------------------------------
// MARK: - LoggerImplMulti
// ----------------------------------------------------------------------------------------------------

template <typename LoggerT>
Logger* LoggerImplMulti::findType()
{
	for( const auto &logger : mLoggers ) {
		if( dynamic_cast<LoggerT *>( logger.get() ) )
			return logger.get();
	}

	return nullptr;
}

void LoggerImplMulti::remove( Logger *logger )
{
	mLoggers.erase( remove_if( mLoggers.begin(), mLoggers.end(),
							  [logger]( const std::unique_ptr<Logger> &o ) {
								  return o.get() == logger;
							  } ),
				   mLoggers.end() );
}

void LoggerImplMulti::write( const Metadata &meta, const string &text )
{
	for( auto &logger : mLoggers )
		logger->write( meta, text );
}

// ----------------------------------------------------------------------------------------------------
// MARK: - LoggerFile
// ----------------------------------------------------------------------------------------------------

LoggerFile::LoggerFile( const fs::path &filePath )
	: mFilePath( filePath )
{
	if( mFilePath.empty() )
		mFilePath = DEFAULT_FILE_LOG_PATH;

	setTimestampEnabled();
}

LoggerFile::~LoggerFile()
{
	if( mStream.is_open() )
		mStream.close();
}

void LoggerFile::write( const Metadata &meta, const string &text )
{
	if( !mStream.is_open() )
		mStream.open( mFilePath.string() );

	writeDefault( mStream, meta, text );
}

#if defined( CINDER_COCOA )

// ----------------------------------------------------------------------------------------------------
// MARK: - LoggerNSLog
// ----------------------------------------------------------------------------------------------------

void LoggerNSLog::write( const Metadata &meta, const string &text )
{
	NSString *textNs = [NSString stringWithCString:text.c_str() encoding:NSUTF8StringEncoding];
	NSString *metaDataNs = [NSString stringWithCString:meta.toString().c_str() encoding:NSUTF8StringEncoding];
	NSLog( @"%@%@", metaDataNs, textNs );
}

#endif

// ----------------------------------------------------------------------------------------------------
// MARK: - Helper Classes
// ----------------------------------------------------------------------------------------------------

string Metadata::toString() const
{
	stringstream ss;
	ss << mLevel << " " << mLocation;
	return ss.str();
}

ostream& operator<<( ostream &os, const Location &rhs )
{
	os << rhs.getFunctionName() << "[" << rhs.getLineNumber() << "]";
	return os;
}

ostream& operator<<( ostream &lhs, const Level &rhs )
{
	switch( rhs ) {
		case LEVEL_VERBOSE:		lhs << "|verbose|";	break;
		case LEVEL_INFO:		lhs << "|info   |";	break;
		case LEVEL_WARNING:		lhs << "|warning|";	break;
		case LEVEL_ERROR:		lhs << "|error  |";	break;
		case LEVEL_FATAL:		lhs << "|fatal  |";	break;
		default: CI_ASSERT_NOT_REACHABLE();
	}
	return lhs;
}

} } // namespace cinder::log
<|endoftext|>


<commit_before>#if !defined(DEBUGGER_DISABLE_LAUNCH)

#include <lua.hpp>
#include <string>
#include "dbg_impl.h"
#include "dbg_unicode.h"
#include "dbg_format.h"
#include "dbg_io.h"
#include "dbg_delayload.h"

namespace vscode
{
	static int errfunc(lua_State* L) {
		debugger_impl* dbg = (debugger_impl*)lua_touserdata(L, lua_upvalueindex(1));
		luaL_traceback(L, L, lua_tostring(L, 1), 1);
		dbg->exception(L, lua_tostring(L, -1));
		lua_settop(L, 2);
		return 1;
	}

	bool debugger_impl::request_launch(rprotocol& req) {
		cache_launch_ = rprotocol();
		if (!is_state(state::initialized)) {
			response_error(req, "not initialized or unexpected state");
			return false;
		}
		auto& args = req["arguments"];
		if (args.HasMember("runtimeExecutable") && args["runtimeExecutable"].IsString()) {
			cache_launch_ = std::move(req);
			return false;
		}
		if (!args.HasMember("program") || !args["program"].IsString()) {
			response_error(req, "Launch failed");
			return false;
		}
#if defined(DEBUGGER_DELAYLOAD_LUA)
		if (args.HasMember("luadll")) {
			delayload::set_luadll(vscode::u2w(args["luadll"].Get<std::string>()));
		}
#endif
		initialize_sourcemaps(args);
		if (args.HasMember("cwd") && args["cwd"].IsString()) {
			fs::current_path(fs::path(u2w(args["cwd"].Get<std::string>())));
		}
		if (args.HasMember("env") && args["env"].IsObject()) {
			for (auto& v : args["env"].GetObject()) {
				if (v.name.IsString()) {
					if (v.value.IsString()) {
						::SetEnvironmentVariableW(vscode::u2w(v.name.Get<std::string>()).c_str(), vscode::u2w(v.value.Get<std::string>()).c_str());
					}
					else if (v.value.IsNull()) {
						::SetEnvironmentVariableW(vscode::u2w(v.name.Get<std::string>()).c_str(), NULL);
					}
				}
			}
		}
		cache_launch_ = std::move(req);
		return false;
	}

	bool debugger_impl::request_launch_done(rprotocol& req) {
		auto& args = req["arguments"];
		if (args.HasMember("runtimeExecutable") && args["runtimeExecutable"].IsString()) {
			if (attachL_)
				init_redirector(req, attachL_);
			return request_attach(req);
		}
		if (launchL_) {
			lua_close(launchL_);
			launchL_ = 0;
		}
		lua_State* L = luaL_newstate();
		luaL_openlibs(L);
		init_redirector(req, L);

		if (args.HasMember("path") && args["path"].IsString())
		{
			std::string path = u2a(args["path"]);
			lua_getglobal(L, "package");
			lua_pushlstring(L, path.data(), path.size());
			lua_setfield(L, -2, "path");
			lua_pop(L, 1);
		}
		if (args.HasMember("cpath") && args["cpath"].IsString())
		{
			std::string path = u2a(args["cpath"]);
			lua_getglobal(L, "package");
			lua_pushlstring(L, path.data(), path.size());
			lua_setfield(L, -2, "cpath");
			lua_pop(L, 1);
		}
		lua_newtable(L);
		if (args.HasMember("arg0")) {
			if (args["arg0"].IsString()) {
				auto& v = args["arg0"];
				lua_pushlstring(L, v.GetString(), v.GetStringLength());
				lua_rawseti(L, -2, 0);
			}
			else if (args["arg0"].IsArray()) {
				int i = 1 - (int)args["arg0"].Size();
				for (auto& v : args["arg0"].GetArray())
				{
					if (v.IsString()) {
						lua_pushlstring(L, v.GetString(), v.GetStringLength());
					}
					else {
						lua_pushstring(L, "");
					}
					lua_rawseti(L, -2, i++);
				}
			}
		}
		if (args.HasMember("arg") && args["arg"].IsArray()) {
			int i = 1;
			for (auto& v : args["arg"].GetArray())
			{
				if (v.IsString()) {
					lua_pushlstring(L, v.GetString(), v.GetStringLength());
				}
				else {
					lua_pushstring(L, "");
				}
				lua_rawseti(L, -2, i++);
			}
		}
		lua_setglobal(L, "arg");

		std::string program = u2a(args["program"]);
		int status = luaL_loadfile(L, program.c_str());
		if (status != LUA_OK) {
			event_output("console", format("Failed to launch %s due to error: %s\n", program, lua_tostring(L, -1)));
			response_error(req, "Launch failed");
			lua_pop(L, 1);
			return false;
		}
		else
		{
			response_success(req);
		}

		bool stopOnEntry = true;
		if (args.HasMember("stopOnEntry") && args["stopOnEntry"].IsBool()) {
			stopOnEntry = args["stopOnEntry"].GetBool();
		}
		event_thread(true);
		if (stopOnEntry)
		{
			set_state(state::stepping);
			event_stopped("entry");
		}
		else
		{
			set_state(state::running);
		}
		open_hook(L);
		launchL_ = L;
		return false;
	}

	void debugger_impl::update_launch()
	{
		if (launchL_)
		{
			lua_State *L = launchL_;
			launchL_ = 0;

			lua_pushlightuserdata(L, this);
			lua_pushcclosure(L, errfunc, 1);
			lua_insert(L, -2);
			if (lua_pcall(L, 0, 0, -2))
			{
				event_output("console", format("Program terminated with error: %s\n", lua_tostring(L, -1)));
				lua_pop(L, 1);
			}
			lua_pop(L, 1);
			set_state(state::terminated);
			network_->close();
			lua_close(L);
		}
	}
}

#endif
<commit_msg>用windows api不能刷新getenv的缓存<commit_after>#if !defined(DEBUGGER_DISABLE_LAUNCH)

#include <lua.hpp>
#include <string>
#include "dbg_impl.h"
#include "dbg_unicode.h"
#include "dbg_format.h"
#include "dbg_io.h"
#include "dbg_delayload.h"

namespace vscode
{
	static int errfunc(lua_State* L) {
		debugger_impl* dbg = (debugger_impl*)lua_touserdata(L, lua_upvalueindex(1));
		luaL_traceback(L, L, lua_tostring(L, 1), 1);
		dbg->exception(L, lua_tostring(L, -1));
		lua_settop(L, 2);
		return 1;
	}

	bool debugger_impl::request_launch(rprotocol& req) {
		cache_launch_ = rprotocol();
		if (!is_state(state::initialized)) {
			response_error(req, "not initialized or unexpected state");
			return false;
		}
		auto& args = req["arguments"];
		if (args.HasMember("runtimeExecutable") && args["runtimeExecutable"].IsString()) {
			cache_launch_ = std::move(req);
			return false;
		}
		if (!args.HasMember("program") || !args["program"].IsString()) {
			response_error(req, "Launch failed");
			return false;
		}
#if defined(DEBUGGER_DELAYLOAD_LUA)
		if (args.HasMember("luadll")) {
			delayload::set_luadll(vscode::u2w(args["luadll"].Get<std::string>()));
		}
#endif
		initialize_sourcemaps(args);
		if (args.HasMember("cwd") && args["cwd"].IsString()) {
			fs::current_path(fs::path(u2w(args["cwd"].Get<std::string>())));
		}
		if (args.HasMember("env") && args["env"].IsObject()) {
			for (auto& v : args["env"].GetObject()) {
				if (v.name.IsString()) {
					if (v.value.IsString()) {
						_wputenv((vscode::u2w(v.name.Get<std::string>()) + L"=" + vscode::u2w(v.value.Get<std::string>())).c_str());
					}
					else if (v.value.IsNull()) {
						_wputenv((vscode::u2w(v.name.Get<std::string>()) + L"=").c_str());
					}
				}
			}
		}
		cache_launch_ = std::move(req);
		return false;
	}

	bool debugger_impl::request_launch_done(rprotocol& req) {
		auto& args = req["arguments"];
		if (args.HasMember("runtimeExecutable") && args["runtimeExecutable"].IsString()) {
			if (attachL_)
				init_redirector(req, attachL_);
			return request_attach(req);
		}
		if (launchL_) {
			lua_close(launchL_);
			launchL_ = 0;
		}
		lua_State* L = luaL_newstate();
		luaL_openlibs(L);
		init_redirector(req, L);

		if (args.HasMember("path") && args["path"].IsString())
		{
			std::string path = u2a(args["path"]);
			lua_getglobal(L, "package");
			lua_pushlstring(L, path.data(), path.size());
			lua_setfield(L, -2, "path");
			lua_pop(L, 1);
		}
		if (args.HasMember("cpath") && args["cpath"].IsString())
		{
			std::string path = u2a(args["cpath"]);
			lua_getglobal(L, "package");
			lua_pushlstring(L, path.data(), path.size());
			lua_setfield(L, -2, "cpath");
			lua_pop(L, 1);
		}
		lua_newtable(L);
		if (args.HasMember("arg0")) {
			if (args["arg0"].IsString()) {
				auto& v = args["arg0"];
				lua_pushlstring(L, v.GetString(), v.GetStringLength());
				lua_rawseti(L, -2, 0);
			}
			else if (args["arg0"].IsArray()) {
				int i = 1 - (int)args["arg0"].Size();
				for (auto& v : args["arg0"].GetArray())
				{
					if (v.IsString()) {
						lua_pushlstring(L, v.GetString(), v.GetStringLength());
					}
					else {
						lua_pushstring(L, "");
					}
					lua_rawseti(L, -2, i++);
				}
			}
		}
		if (args.HasMember("arg") && args["arg"].IsArray()) {
			int i = 1;
			for (auto& v : args["arg"].GetArray())
			{
				if (v.IsString()) {
					lua_pushlstring(L, v.GetString(), v.GetStringLength());
				}
				else {
					lua_pushstring(L, "");
				}
				lua_rawseti(L, -2, i++);
			}
		}
		lua_setglobal(L, "arg");

		std::string program = u2a(args["program"]);
		int status = luaL_loadfile(L, program.c_str());
		if (status != LUA_OK) {
			event_output("console", format("Failed to launch %s due to error: %s\n", program, lua_tostring(L, -1)));
			response_error(req, "Launch failed");
			lua_pop(L, 1);
			return false;
		}
		else
		{
			response_success(req);
		}

		bool stopOnEntry = true;
		if (args.HasMember("stopOnEntry") && args["stopOnEntry"].IsBool()) {
			stopOnEntry = args["stopOnEntry"].GetBool();
		}
		event_thread(true);
		if (stopOnEntry)
		{
			set_state(state::stepping);
			event_stopped("entry");
		}
		else
		{
			set_state(state::running);
		}
		open_hook(L);
		launchL_ = L;
		return false;
	}

	void debugger_impl::update_launch()
	{
		if (launchL_)
		{
			lua_State *L = launchL_;
			launchL_ = 0;

			lua_pushlightuserdata(L, this);
			lua_pushcclosure(L, errfunc, 1);
			lua_insert(L, -2);
			if (lua_pcall(L, 0, 0, -2))
			{
				event_output("console", format("Program terminated with error: %s\n", lua_tostring(L, -1)));
				lua_pop(L, 1);
			}
			lua_pop(L, 1);
			set_state(state::terminated);
			network_->close();
			lua_close(L);
		}
	}
}

#endif
<|endoftext|>


<commit_before>/*
* Copyright (C) 2008-2011 J-P Nurmi <jpnurmi@gmail.com>
* Copyright (C) 2010-2011 SmokeX <smokexjc@gmail.com>
*
* 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 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.
*/

#include "ircsession.h"
#include "ircsession_p.h"
#include "irccommand.h"
#include "ircmessage.h"
#include "ircutil.h"
#include "irc.h"
#include <QTcpSocket>
#include <QStringList>

/*!
    \class IrcSession ircsession.h
    \brief The IrcSession class provides an IRC session.

    IRC (Internet Relay Chat protocol) is a simple communication protocol.
    IrcSession provides means to establish a connection to an IRC server.
    IrcSession works asynchronously. None of the functions block the
    calling thread but they return immediately and the actual work is done
    behind the scenes in the event loop.

    Example usage:
    \code
    IrcSession* session = new IrcSession(this);
    session->setNick("jpnurmi");
    session->setIdent("jpn");
    session->setRealName("J-P Nurmi");
    session->connectToServer("irc.freenode.net", 6667);
    \endcode

    \note IrcSession supports SSL (Secure Sockets Layer) connections since version 0.3.0

    Example SSL usage:
    \code
    IrcSession* session = new IrcSession(this);
    // ...
    QSslSocket* socket = new QSslSocket(session);
    socket->ignoreSslErrors();
    socket->setPeerVerifyMode(QSslSocket::VerifyNone);
    session->setSocket(socket);
    session->connectToServer("irc.secure.ssl", 6669);
    \endcode

    \sa setSocket()
 */

/*!
    \fn void IrcSession::connecting()

    This signal is emitted when the connection is being established.
 */

/*!
    \fn void IrcSession::connected()

    This signal is emitted when the welcome message has been received.

    \sa Irc::RPL_WELCOME
 */

/*!
    \fn void IrcSession::disconnected()

    This signal is emitted when the session has been disconnected.
 */

IrcSessionPrivate::IrcSessionPrivate(IrcSession* session) :
    q_ptr(session),
    parser(),
    buffer(),
    socket(0),
    host(),
    port(6667),
    userName(),
    nickName(),
    realName()
{
}

void IrcSessionPrivate::_q_connected()
{
    Q_Q(IrcSession);
    emit q->connecting();

    QString password;
    emit q->password(&password);
    if (!password.isEmpty())
        q->sendCommand(IrcCommand::createPassword(password));

    q->sendCommand(IrcCommand::createNick(nickName));
    q->sendCommand(IrcCommand::createUser(userName, realName));
}

void IrcSessionPrivate::_q_disconnected()
{
    Q_Q(IrcSession);
    emit q->disconnected();
}

void IrcSessionPrivate::_q_reconnect()
{
    if (socket)
    {
        socket->connectToHost(host, port);
        if (socket->inherits("QSslSocket"))
            QMetaObject::invokeMethod(socket, "startClientEncryption");
    }
}

void IrcSessionPrivate::_q_error(QAbstractSocket::SocketError error)
{
    qDebug() << "IrcSessionPrivate::_q_error():" << error;
}

void IrcSessionPrivate::_q_state(QAbstractSocket::SocketState state)
{
    qDebug() << "IrcSessionPrivate::_q_state():" << state;
}

void IrcSessionPrivate::_q_readData()
{
    buffer += socket->readAll();
    // try reading RFC compliant message lines first
    readLines("\r\n");
    // fall back to RFC incompliant lines...
    readLines("\n");
}

void IrcSessionPrivate::readLines(const QByteArray& delimiter)
{
    int i = -1;
    while ((i = buffer.indexOf(delimiter)) != -1)
    {
        QByteArray line = buffer.left(i).trimmed();
        buffer = buffer.mid(i + delimiter.length());
        if (!line.isEmpty())
            processLine(line);
    }
}

void IrcSessionPrivate::processLine(const QByteArray& line)
{
    Q_Q(IrcSession);
    parser.parse(line);

    qDebug() << line;

    QString prefix = parser.prefix();
    QString command = parser.command();
    QStringList params = parser.params();

    IrcMessage* msg = IrcMessage::create(command);
    if (msg)
    {
        msg->initFrom(prefix, params);

        switch (msg->type())
        {
        case IrcMessage::Numeric:
            if (static_cast<IrcNumericMessage*>(msg)->code() == Irc::RPL_WELCOME)
                emit q->connected();
            break;
        case IrcMessage::Ping: {
            QString target = static_cast<IrcPingMessage*>(msg)->target();
            IrcCommand* pongCmd = IrcCommand::createPong(target);
            q->sendCommand(pongCmd);
            break;
            }
        default:
            break;
        }

        emit q->messageReceived(msg);
    }
}

bool IrcSessionPrivate::isConnected() const
{
    return socket &&
        (socket->state() == QAbstractSocket::ConnectingState
         || socket->state() == QAbstractSocket::ConnectedState);
}

/*!
    Constructs a new IRC session with \a parent.
 */
IrcSession::IrcSession(QObject* parent) : QObject(parent), d_ptr(new IrcSessionPrivate(this))
{
    setSocket(new QTcpSocket(this));
}

/*!
    Destructs the IRC session.
 */
IrcSession::~IrcSession()
{
    Q_D(IrcSession);
    if (d->socket)
        d->socket->close();
}

/*!
    Returns the encoding.

    The default value is a null QByteArray.
 */
QByteArray IrcSession::encoding() const
{
    Q_D(const IrcSession);
    return d->parser.encoding();
}

/*!
    Sets the \a encoding.

    See QTextCodec documentation for supported encodings.

    Encoding auto-detection can be turned on by passing a null QByteArray.

    The fallback locale is QTextCodec::codecForLocale().
 */
void IrcSession::setEncoding(const QByteArray& encoding)
{
    Q_D(IrcSession);
    d->parser.setEncoding(encoding);
}

/*!
    Returns the host.
 */
QString IrcSession::host() const
{
    Q_D(const IrcSession);
    return d->host;
}

/*!
    Sets the \a host.
 */
void IrcSession::setHost(const QString& host)
{
    Q_D(IrcSession);
    if (d->isConnected())
        qWarning("IrcSession::setHost() has no effect until re-connect");
    d->host = host;
}

/*!
    Returns the port.
 */
int IrcSession::port() const
{
    Q_D(const IrcSession);
    return d->port;
}

/*!
    Sets the \a port.
 */
void IrcSession::setPort(int port)
{
    Q_D(IrcSession);
    if (d->isConnected())
        qWarning("IrcSession::setPort() has no effect until re-connect");
    d->port = port;
}

/*!
    Returns the user name.
 */
QString IrcSession::userName() const
{
    Q_D(const IrcSession);
    return d->userName;
}

/*!
    Sets the user \a name.

    \note setUserName() has no effect on already established connection.
 */
void IrcSession::setUserName(const QString& name)
{
    Q_D(IrcSession);
    if (d->isConnected())
        qWarning("IrcSession::setUserName() has no effect until re-connect");
    d->userName = name.split(" ", QString::SkipEmptyParts).value(0).trimmed();
}

/*!
    Returns the nick name.
 */
QString IrcSession::nickName() const
{
    Q_D(const IrcSession);
    return d->nickName;
}

/*!
    Sets the nick \a name.
 */
void IrcSession::setNickName(const QString& name)
{
    Q_D(IrcSession);
    QString nick = name.split(" ", QString::SkipEmptyParts).value(0).trimmed();
    if (d->nickName != nick)
    {
        d->nickName = nick;
        if (d->isConnected())
            sendCommand(IrcCommand::createNick(nick));
    }
}

/*!
    Returns the real name.
 */
QString IrcSession::realName() const
{
    Q_D(const IrcSession);
    return d->realName;
}

/*!
    Sets the real \a name.

    \note setRealName() has no effect on already established connection.
 */
void IrcSession::setRealName(const QString& name)
{
    Q_D(IrcSession);
    if (d->isConnected())
        qWarning("IrcSession::setRealName() has no effect until re-connect");
    d->realName = name;
}

/*!
    Returns the socket.

    IrcSession creates an instance of QTcpSocket by default.

    This function was introduced in version 0.3.0.
 */
QAbstractSocket* IrcSession::socket() const
{
    Q_D(const IrcSession);
    return d->socket;
}

/*!
    Sets the \a socket. The previously set socket is deleted if its parent is \c this.

    IrcSession supports QSslSocket in the way that it automatically calls
    QSslSocket::startClientEncryption() while connecting.

    This function was introduced in version 0.3.0.
 */
void IrcSession::setSocket(QAbstractSocket* socket)
{
    Q_D(IrcSession);
    if (d->socket != socket)
    {
        if (d->socket)
        {
            d->socket->disconnect(this);
            if (d->socket->parent() == this)
                d->socket->deleteLater();
        }

        d->socket = socket;
        if (socket)
        {
            connect(socket, SIGNAL(connected()), this, SLOT(_q_connected()));
            connect(socket, SIGNAL(disconnected()), this, SLOT(_q_disconnected()));
            connect(socket, SIGNAL(readyRead()), this, SLOT(_q_readData()));
            connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(_q_error(QAbstractSocket::SocketError)));
            connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(_q_state(QAbstractSocket::SocketState)));
        }
    }
}

/*!
    Connects to the server.
 */
void IrcSession::open()
{
    Q_D(IrcSession);
    if (d->userName.isEmpty())
    {
        qCritical("IrcSession::open(): userName is empty!");
        return;
    }
    if (d->nickName.isEmpty())
    {
        qCritical("IrcSession::open(): nickName is empty!");
        return;
    }
    if (d->realName.isEmpty())
    {
        qCritical("IrcSession::open(): realName is empty!");
        return;
    }
    d->_q_reconnect();
}

/*!
    Disconnects from the server.
 */
void IrcSession::close()
{
    Q_D(IrcSession);
    if (d->socket)
        d->socket->disconnectFromHost();
}

/*!
    Sends a \a command to the server.

    The command must be allocated on the heap since the session will take
    ownership of the command and delete it once it has been sent. It is
    not safe to access the command after it has been sent.

    \sa sendRaw()
 */
bool IrcSession::sendCommand(IrcCommand* command)
{
    return command && sendRaw(command->toString());
}

/*!
    Sends a raw \a message to the server.

    \sa sendCommand()
 */
bool IrcSession::sendRaw(const QString& message)
{
    Q_D(IrcSession);
    qint64 bytes = -1;
    if (d->socket)
        bytes = d->socket->write(message.toUtf8() + QByteArray("\r\n"));
    return bytes != -1;
}

#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug debug, const IrcSession* session)
{
    if (!session)
        return debug << "IrcSession(0x0) ";
    debug.nospace() << session->metaObject()->className() << '(' << (void*) session;
    if (!session->objectName().isEmpty())
        debug << ", name = " << session->objectName();
    if (!session->host().isEmpty())
        debug << ", host = " << session->host()
              << ", port = " << session->port();
    debug << ')';
    return debug.space();
}
#endif // QT_NO_DEBUG_STREAM

#include "moc_ircsession.cpp"
<commit_msg>Hide libcommuni debug output behind COMMUNI_DEBUG=1 environment variable<commit_after>/*
* Copyright (C) 2008-2011 J-P Nurmi <jpnurmi@gmail.com>
* Copyright (C) 2010-2011 SmokeX <smokexjc@gmail.com>
*
* 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 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.
*/

#include "ircsession.h"
#include "ircsession_p.h"
#include "irccommand.h"
#include "ircmessage.h"
#include "ircutil.h"
#include "irc.h"
#include <QTcpSocket>
#include <QStringList>

/*!
    \class IrcSession ircsession.h
    \brief The IrcSession class provides an IRC session.

    IRC (Internet Relay Chat protocol) is a simple communication protocol.
    IrcSession provides means to establish a connection to an IRC server.
    IrcSession works asynchronously. None of the functions block the
    calling thread but they return immediately and the actual work is done
    behind the scenes in the event loop.

    Example usage:
    \code
    IrcSession* session = new IrcSession(this);
    session->setNick("jpnurmi");
    session->setIdent("jpn");
    session->setRealName("J-P Nurmi");
    session->connectToServer("irc.freenode.net", 6667);
    \endcode

    \note IrcSession supports SSL (Secure Sockets Layer) connections since version 0.3.0

    Example SSL usage:
    \code
    IrcSession* session = new IrcSession(this);
    // ...
    QSslSocket* socket = new QSslSocket(session);
    socket->ignoreSslErrors();
    socket->setPeerVerifyMode(QSslSocket::VerifyNone);
    session->setSocket(socket);
    session->connectToServer("irc.secure.ssl", 6669);
    \endcode

    \sa setSocket()
 */

/*!
    \fn void IrcSession::connecting()

    This signal is emitted when the connection is being established.
 */

/*!
    \fn void IrcSession::connected()

    This signal is emitted when the welcome message has been received.

    \sa Irc::RPL_WELCOME
 */

/*!
    \fn void IrcSession::disconnected()

    This signal is emitted when the session has been disconnected.
 */

IrcSessionPrivate::IrcSessionPrivate(IrcSession* session) :
    q_ptr(session),
    parser(),
    buffer(),
    socket(0),
    host(),
    port(6667),
    userName(),
    nickName(),
    realName()
{
}

void IrcSessionPrivate::_q_connected()
{
    Q_Q(IrcSession);
    emit q->connecting();

    QString password;
    emit q->password(&password);
    if (!password.isEmpty())
        q->sendCommand(IrcCommand::createPassword(password));

    q->sendCommand(IrcCommand::createNick(nickName));
    q->sendCommand(IrcCommand::createUser(userName, realName));
}

void IrcSessionPrivate::_q_disconnected()
{
    Q_Q(IrcSession);
    emit q->disconnected();
}

void IrcSessionPrivate::_q_reconnect()
{
    if (socket)
    {
        socket->connectToHost(host, port);
        if (socket->inherits("QSslSocket"))
            QMetaObject::invokeMethod(socket, "startClientEncryption");
    }
}

void IrcSessionPrivate::_q_error(QAbstractSocket::SocketError error)
{
    qCritical() << "IrcSessionPrivate::_q_error():" << error;
}

void IrcSessionPrivate::_q_state(QAbstractSocket::SocketState state)
{
    static bool dbg = qgetenv("COMMUNI_DEBUG").toInt();
    if (dbg) qDebug() << "IrcSessionPrivate::_q_state():" << state;
}

void IrcSessionPrivate::_q_readData()
{
    buffer += socket->readAll();
    // try reading RFC compliant message lines first
    readLines("\r\n");
    // fall back to RFC incompliant lines...
    readLines("\n");
}

void IrcSessionPrivate::readLines(const QByteArray& delimiter)
{
    int i = -1;
    while ((i = buffer.indexOf(delimiter)) != -1)
    {
        QByteArray line = buffer.left(i).trimmed();
        buffer = buffer.mid(i + delimiter.length());
        if (!line.isEmpty())
            processLine(line);
    }
}

void IrcSessionPrivate::processLine(const QByteArray& line)
{
    Q_Q(IrcSession);
    parser.parse(line);

    static bool dbg = qgetenv("COMMUNI_DEBUG").toInt();
    if (dbg) qDebug() << line;

    QString prefix = parser.prefix();
    QString command = parser.command();
    QStringList params = parser.params();

    IrcMessage* msg = IrcMessage::create(command);
    if (msg)
    {
        msg->initFrom(prefix, params);

        switch (msg->type())
        {
        case IrcMessage::Numeric:
            if (static_cast<IrcNumericMessage*>(msg)->code() == Irc::RPL_WELCOME)
                emit q->connected();
            break;
        case IrcMessage::Ping: {
            QString target = static_cast<IrcPingMessage*>(msg)->target();
            IrcCommand* pongCmd = IrcCommand::createPong(target);
            q->sendCommand(pongCmd);
            break;
            }
        default:
            break;
        }

        emit q->messageReceived(msg);
    }
}

bool IrcSessionPrivate::isConnected() const
{
    return socket &&
        (socket->state() == QAbstractSocket::ConnectingState
         || socket->state() == QAbstractSocket::ConnectedState);
}

/*!
    Constructs a new IRC session with \a parent.
 */
IrcSession::IrcSession(QObject* parent) : QObject(parent), d_ptr(new IrcSessionPrivate(this))
{
    setSocket(new QTcpSocket(this));
}

/*!
    Destructs the IRC session.
 */
IrcSession::~IrcSession()
{
    Q_D(IrcSession);
    if (d->socket)
        d->socket->close();
}

/*!
    Returns the encoding.

    The default value is a null QByteArray.
 */
QByteArray IrcSession::encoding() const
{
    Q_D(const IrcSession);
    return d->parser.encoding();
}

/*!
    Sets the \a encoding.

    See QTextCodec documentation for supported encodings.

    Encoding auto-detection can be turned on by passing a null QByteArray.

    The fallback locale is QTextCodec::codecForLocale().
 */
void IrcSession::setEncoding(const QByteArray& encoding)
{
    Q_D(IrcSession);
    d->parser.setEncoding(encoding);
}

/*!
    Returns the host.
 */
QString IrcSession::host() const
{
    Q_D(const IrcSession);
    return d->host;
}

/*!
    Sets the \a host.
 */
void IrcSession::setHost(const QString& host)
{
    Q_D(IrcSession);
    if (d->isConnected())
        qWarning("IrcSession::setHost() has no effect until re-connect");
    d->host = host;
}

/*!
    Returns the port.
 */
int IrcSession::port() const
{
    Q_D(const IrcSession);
    return d->port;
}

/*!
    Sets the \a port.
 */
void IrcSession::setPort(int port)
{
    Q_D(IrcSession);
    if (d->isConnected())
        qWarning("IrcSession::setPort() has no effect until re-connect");
    d->port = port;
}

/*!
    Returns the user name.
 */
QString IrcSession::userName() const
{
    Q_D(const IrcSession);
    return d->userName;
}

/*!
    Sets the user \a name.

    \note setUserName() has no effect on already established connection.
 */
void IrcSession::setUserName(const QString& name)
{
    Q_D(IrcSession);
    if (d->isConnected())
        qWarning("IrcSession::setUserName() has no effect until re-connect");
    d->userName = name.split(" ", QString::SkipEmptyParts).value(0).trimmed();
}

/*!
    Returns the nick name.
 */
QString IrcSession::nickName() const
{
    Q_D(const IrcSession);
    return d->nickName;
}

/*!
    Sets the nick \a name.
 */
void IrcSession::setNickName(const QString& name)
{
    Q_D(IrcSession);
    QString nick = name.split(" ", QString::SkipEmptyParts).value(0).trimmed();
    if (d->nickName != nick)
    {
        d->nickName = nick;
        if (d->isConnected())
            sendCommand(IrcCommand::createNick(nick));
    }
}

/*!
    Returns the real name.
 */
QString IrcSession::realName() const
{
    Q_D(const IrcSession);
    return d->realName;
}

/*!
    Sets the real \a name.

    \note setRealName() has no effect on already established connection.
 */
void IrcSession::setRealName(const QString& name)
{
    Q_D(IrcSession);
    if (d->isConnected())
        qWarning("IrcSession::setRealName() has no effect until re-connect");
    d->realName = name;
}

/*!
    Returns the socket.

    IrcSession creates an instance of QTcpSocket by default.

    This function was introduced in version 0.3.0.
 */
QAbstractSocket* IrcSession::socket() const
{
    Q_D(const IrcSession);
    return d->socket;
}

/*!
    Sets the \a socket. The previously set socket is deleted if its parent is \c this.

    IrcSession supports QSslSocket in the way that it automatically calls
    QSslSocket::startClientEncryption() while connecting.

    This function was introduced in version 0.3.0.
 */
void IrcSession::setSocket(QAbstractSocket* socket)
{
    Q_D(IrcSession);
    if (d->socket != socket)
    {
        if (d->socket)
        {
            d->socket->disconnect(this);
            if (d->socket->parent() == this)
                d->socket->deleteLater();
        }

        d->socket = socket;
        if (socket)
        {
            connect(socket, SIGNAL(connected()), this, SLOT(_q_connected()));
            connect(socket, SIGNAL(disconnected()), this, SLOT(_q_disconnected()));
            connect(socket, SIGNAL(readyRead()), this, SLOT(_q_readData()));
            connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(_q_error(QAbstractSocket::SocketError)));
            connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(_q_state(QAbstractSocket::SocketState)));
        }
    }
}

/*!
    Connects to the server.
 */
void IrcSession::open()
{
    Q_D(IrcSession);
    if (d->userName.isEmpty())
    {
        qCritical("IrcSession::open(): userName is empty!");
        return;
    }
    if (d->nickName.isEmpty())
    {
        qCritical("IrcSession::open(): nickName is empty!");
        return;
    }
    if (d->realName.isEmpty())
    {
        qCritical("IrcSession::open(): realName is empty!");
        return;
    }
    d->_q_reconnect();
}

/*!
    Disconnects from the server.
 */
void IrcSession::close()
{
    Q_D(IrcSession);
    if (d->socket)
        d->socket->disconnectFromHost();
}

/*!
    Sends a \a command to the server.

    The command must be allocated on the heap since the session will take
    ownership of the command and delete it once it has been sent. It is
    not safe to access the command after it has been sent.

    \sa sendRaw()
 */
bool IrcSession::sendCommand(IrcCommand* command)
{
    return command && sendRaw(command->toString());
}

/*!
    Sends a raw \a message to the server.

    \sa sendCommand()
 */
bool IrcSession::sendRaw(const QString& message)
{
    Q_D(IrcSession);
    qint64 bytes = -1;
    if (d->socket)
        bytes = d->socket->write(message.toUtf8() + QByteArray("\r\n"));
    return bytes != -1;
}

#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug debug, const IrcSession* session)
{
    if (!session)
        return debug << "IrcSession(0x0) ";
    debug.nospace() << session->metaObject()->className() << '(' << (void*) session;
    if (!session->objectName().isEmpty())
        debug << ", name = " << session->objectName();
    if (!session->host().isEmpty())
        debug << ", host = " << session->host()
              << ", port = " << session->port();
    debug << ')';
    return debug.space();
}
#endif // QT_NO_DEBUG_STREAM

#include "moc_ircsession.cpp"
<|endoftext|>


<commit_before>// Do not remove the include below
#include "SynchroClock.h"

#ifdef DS3231
RtcDS3231<TwoWire> rtc(Wire);
#endif
#ifdef DS1307
RtcDS1307<TwoWire> rtc(Wire);
#endif

FeedbackLED feedback(LED_PIN);
ESP8266WebServer HTTP(80);
SNTP ntp("time.apple.com", 123);
bool syncing = false;
uint8_t last_pin = 0;

uint16_t getValidPosition(String name)
{
    int result = 0;

    char value[10];
    strncpy(value, HTTP.arg(name).c_str(), 9);
    if (strchr(value, ':') != NULL)
    {
        char* s = strtok(value, ":");

        if (s != NULL) {
            result += atoi(s) * 3600; // hours to seconds
            s = strtok(NULL, ":");
        }
        if (s != NULL) {
            result += atoi(s) * 60; // minutes to seconds
            s = strtok(NULL, ":");
        }
        if (s != NULL) {
            result += atoi(s);
        }
    }
    else
    {
        result = atoi(value);
        if (result < 0 || result > 43199)
        {
            Serial.println(
                    "invalid value for " + name + ": " + HTTP.arg(name)
                    + " using 0 instead!");
            result = 0;
        }
    }
    return result;
}

uint8_t getValidDuration(String name)
{
  int i = HTTP.arg(name).toInt();
  if (i < 0 || i > 255)
  {
    Serial.println(
        "invalid value for " + name + ": " + HTTP.arg(name)
            + " using 32 instead!");
    i = 32;
  }
  return (uint8_t)i;
}

boolean getValidBoolean(String name)
{
  String value = HTTP.arg(name);
  return value.equalsIgnoreCase("true");
}

void handleAdjustment()
{
  uint16_t adj;
  if (HTTP.hasArg("set"))
  {
    if (HTTP.arg("set").equalsIgnoreCase("auto"))
    {
      Serial.println("Auto Adjust!");
      syncClockToRTC();
    }
    else
    {
      adj = getValidPosition("set");
      Serial.print("setting adjustment:");
      Serial.println(adj);
      setClockAdjustment(adj);
    }
  }

  adj = getClockAdjustment();

  HTTP.send(200, "text/plain", String(adj));
}

void handlePosition()
{
  uint16_t pos;
  if (HTTP.hasArg("set"))
  {
    pos = getValidPosition("set");
    Serial.print("setting position:");
    Serial.println(pos);
    setClockPosition(pos);
  }

  pos = getClockPosition();

  int hours = pos / 3600;
  int minutes = (pos - (hours*3600)) / 60;
  int seconds = pos - (hours*3600) - (minutes*60);
  char message[64];
  sprintf(message, "%d (%02d:%02d:%02d)\n", pos, hours, minutes, seconds);
  HTTP.send(200, "text/Plain", message);
}

void handleTPDuration()
{
  uint8_t value;
  if (HTTP.hasArg("set"))
  {
    value = getValidDuration("set");
    Serial.print("setting tp_duration:");
    Serial.println(value);
    setClockTPDuration(value);
  }

  value = getClockTPDuration();

  HTTP.send(200, "text/plain", String(value));
}

void handleAPDuration()
{
  uint8_t value;
  if (HTTP.hasArg("set"))
  {
    value = getValidDuration("set");
    Serial.print("setting ap_duration:");
    Serial.println(value);
    setClockAPDuration(value);
  }

  value = getClockAPDuration();

  HTTP.send(200, "text/plain", String(value));
}

void handleAPDelay()
{
  uint8_t value;
  if (HTTP.hasArg("set"))
  {
    value = getValidDuration("set");
    Serial.print("setting ap_delay:");
    Serial.println(value);
    setClockAPDelay(value);
  }

  value = getClockAPDelay();

  HTTP.send(200, "text/plain", String(value));
}

void handleEnable()
{
  boolean enable;
  if (HTTP.hasArg("set"))
  {
    enable = getValidBoolean("set");
    setClockEnable(enable);
  }
  enable = getClockEnable();
  HTTP.send(200, "text/Plain", String(enable));
}

void handleRTC()
{
  uint16_t value = getRTCTimeAsPosition();
  HTTP.send(200, "text/plain", String(value));
}

void handleNTP()
{
  Serial.println("disabling the clock!");
  setClockEnable(false);
  syncing = true;
  Serial.println("syncing now true!");
  HTTP.send(200, "text/Plain", "OK\n");
}

void setup()
{
  Serial.begin(115200);
  Serial.println("");
  Serial.println("Startup!");

  pinMode(SYNC_PIN, INPUT);

  rtc.Begin();
  RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);

  if (!rtc.IsDateTimeValid())
  {
    // Common Causes:
    //    1) first time you ran and the device wasn't running yet
    //    2) the battery on the device is low or even missing

    Serial.println("RTC lost confidence in the DateTime!");

    // following line sets the RTC to the date & time this sketch was compiled
    // it will also reset the valid flag internally unless the Rtc device is
    // having an issue

    rtc.SetDateTime(compiled);
  }

  if (!rtc.GetIsRunning())
  {
    Serial.println("RTC was not actively running, starting now");
    rtc.SetIsRunning(true);
  }

  // never assume the Rtc was last configured by you, so
  // just clear them to your needed state
#ifdef DS3231
  rtc.Enable32kHzPin(false);
#endif

  boolean enabled = getClockEnable();
  Serial.println("clock enable is:" + String(enabled));

  if (!enabled)
  {
    Serial.println("set mode none");
    rtc.SetSquareWavePin(SquareWavePin_ModeNone);
#ifdef DS3231
    Serial.println("set 1Hz");
    rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz);
#endif
  }
  else
  {
    Serial.println("clock is enabled, skipping init of RTC");
  }

  // setup wifi, blink let slow while connecting and fast if portal activated.
  feedback.blink(FEEDBACK_LED_SLOW);
  WiFiManager wifi;
  wifi.setAPCallback([](WiFiManager *)
  { feedback.blink(FEEDBACK_LED_FAST);});
  String ssid = "SynchroClock" + String(ESP.getChipId());
  wifi.autoConnect(ssid.c_str(), NULL);
  feedback.off();

  //Serial.println("set 1kHz");
  //rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1kHz);

  if (!enabled)
  {
    Serial.println("setting clock position");
    setClockPosition(0);
    Serial.println("enabling clock");
    setClockEnable(true);
    Serial.println("starting square wave");
#ifdef DS3231
    rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock);
#endif
#ifdef DS1307
    rtc.SetSquareWavePin(DS1307SquareWaveOut_1Hz);
#endif
  }
  else
  {
    Serial.println("clock is enabled, skipping init of clock!");
  }

  Serial.println("starting HTTP");
  HTTP.on("/adjust", HTTP_GET, handleAdjustment);
  HTTP.on("/position", HTTP_GET, handlePosition);
  HTTP.on("/tp_duration", HTTP_GET, handleTPDuration);
  HTTP.on("/ap_duration", HTTP_GET, handleAPDuration);
  HTTP.on("/ap_delay", HTTP_GET, handleAPDelay);
  HTTP.on("/enable", HTTP_GET, handleEnable);
  HTTP.on("/rtc", HTTP_GET, handleRTC);
  HTTP.on("/ntp", HTTP_GET, handleNTP);
  HTTP.begin();
  ntp.begin(1235);
  syncing = false;
  last_pin = 0;
}

void loop()
{
  HTTP.handleClient();
  uint8_t pin = digitalRead(SYNC_PIN);
  if (syncing && last_pin == 1 && pin == 0)
  {
    EpochTime start;
    start.seconds = rtc.GetDateTime().Epoch32Time();
    start.fraction = 0;
    EpochTime end = ntp.getTime(start);

    // compute the delay to the next second

    uint32_t msdelay = 1000 - (((uint64_t) end.fraction * 1000) >> 32);
    Serial.print("msdelay: ");
    Serial.println(msdelay);
    // wait for the next second
    if (msdelay > 0 && msdelay < 1000)
    {
      delay(msdelay);
    }
    RtcDateTime dt(end.seconds + 1); // +1 because we waited for the next second
    rtc.SetDateTime(dt);
    syncing = false;
    last_pin = 0;
    delay(500);
    Serial.println("rtc updated, starting clock!");
    setClockEnable(true);
    Serial.println("syncing clock to RTC");
    syncClockToRTC();
  }
  else
  {
    last_pin = pin;
  }
}

void syncClockToRTC()
{
  uint16_t rtc_pos = getRTCTimeAsPosition();
  Serial.printf("RTC position:%d", rtc_pos);
  uint16_t clock_pos = getClockPosition();
  if (clock_pos != rtc_pos)
  {
    int adj = rtc_pos - clock_pos;
    if (adj < 0)
    {
      adj += 43200;
    }
    Serial.printf("sending adjustment of %d\n", adj);
    setClockAdjustment(adj);
  }
}

//
// RTC functions
//
uint16_t getRTCTimeAsPosition()
{
  RtcDateTime time = rtc.GetDateTime();
  uint16_t hour = time.Hour();
  uint16_t minute = time.Minute();
  uint16_t second = time.Second();
  if (hour > 11)
  {
    hour -= 12;
    Serial.printf("%02d:%02d:%02d (CORRECTED)\n", hour, minute, second);
  }
  else
  {
    Serial.printf("%02d:%02d:%02d\n", hour, minute, second);
  }
  uint16_t position = hour * 60 * 60 + minute * 60 + second;
  return position;
}

#define countof(a) (sizeof(a) / sizeof(a[0]))

void printDateTime(const RtcDateTime& dt)
{
  char datestring[20];

  snprintf_P(datestring, countof(datestring),
      PSTR("%02u/%02u/%04u %02u:%02u:%02u"), dt.Month(), dt.Day(), dt.Year(),
      dt.Hour(), dt.Minute(), dt.Second());
  Serial.print(datestring);
}

//
// access to analog clock controler via i2c
//
uint16_t getClockAdjustment()
{
  return readClock16(CMD_ADJUSTMENT);
}

void setClockAdjustment(uint16_t value)
{
  writeClock16(CMD_ADJUSTMENT, value);
}

uint16_t getClockPosition()
{
  return readClock16(CMD_POSITION);
}

void setClockPosition(uint16_t value)
{
  writeClock16(CMD_POSITION, value);
}

uint8_t getClockTPDuration()
{
  return readClock8(CMD_TP_DURATION);
}

void setClockTPDuration(uint8_t value)
{
  writeClock8(CMD_TP_DURATION, value);
}

uint8_t getClockAPDuration()
{
  return readClock8(CMD_AP_DURATION);
}

void setClockAPDuration(uint8_t value)
{
  writeClock8(CMD_AP_DURATION, value);
}

uint8_t getClockAPDelay()
{
  return readClock8(CMD_AP_DELAY);
}

void setClockAPDelay(uint8_t value)
{
  writeClock8(CMD_AP_DELAY, value);
}

boolean getClockEnable()
{
  Wire.beginTransmission((uint8_t) I2C_ADDRESS);
  Wire.write(CMD_CONTROL);
  Wire.endTransmission();
  Wire.requestFrom((uint8_t) I2C_ADDRESS, (uint8_t) 1);
  uint8_t value = Wire.read();
  return ((value & BIT_ENABLE) == BIT_ENABLE);
}

void setClockEnable(boolean enable)
{
  Wire.beginTransmission(I2C_ADDRESS);
  Wire.write(CMD_CONTROL);
  Wire.endTransmission();

  Wire.requestFrom((uint8_t) I2C_ADDRESS, (uint8_t) 1);
  uint8_t value = Wire.read();

  if (enable)
  {
    value |= BIT_ENABLE;
  }
  else
  {
    value &= ~BIT_ENABLE;
  }

  Wire.beginTransmission(I2C_ADDRESS);
  Wire.write(CMD_CONTROL);
  Wire.write(value);
  Wire.endTransmission();
}

// send a command, read a 16 bit value
uint16_t readClock16(uint8_t command)
{
  Wire.beginTransmission(I2C_ADDRESS);
  Wire.write(command);
  Wire.endTransmission();
  uint16_t value;
  Wire.requestFrom(I2C_ADDRESS, sizeof(value));
  Wire.readBytes((uint8_t*) &value, sizeof(value));
  return (value);
}

// send a command with a 16 bit value
void writeClock16(uint8_t command, uint16_t value)
{
  Wire.beginTransmission(I2C_ADDRESS);
  Wire.write(command);
  Wire.write((uint8_t*) &value, sizeof(value));
  Wire.endTransmission();
}

// send a command, read a 8 bit value
uint8_t readClock8(uint8_t command)
{
  Wire.beginTransmission(I2C_ADDRESS);
  Wire.write(command);
  Wire.endTransmission();
  uint8_t value;
  Wire.requestFrom(I2C_ADDRESS, sizeof(value));
  Wire.readBytes((uint8_t*) &value, sizeof(value));
  return (value);
}

// send a command with a 16 bit value
void writeClock8(uint8_t command, uint8_t value)
{
  Wire.beginTransmission(I2C_ADDRESS);
  Wire.write(command);
  Wire.write(value);
  Wire.endTransmission();
}

<commit_msg>allow specifing noon/midnight as 12:00:00 instead of 00:00:00<commit_after>// Do not remove the include below
#include "SynchroClock.h"

#ifdef DS3231
RtcDS3231<TwoWire> rtc(Wire);
#endif
#ifdef DS1307
RtcDS1307<TwoWire> rtc(Wire);
#endif

FeedbackLED feedback(LED_PIN);
ESP8266WebServer HTTP(80);
SNTP ntp("time.apple.com", 123);
bool syncing = false;
uint8_t last_pin = 0;

uint16_t getValidPosition(String name)
{
    int result = 0;

    char value[10];
    strncpy(value, HTTP.arg(name).c_str(), 9);
    if (strchr(value, ':') != NULL)
    {
        char* s = strtok(value, ":");

        if (s != NULL) {
            int h = atoi(s);
            while (h > 11)
            {
                h -= 12;
            }

            result += h * 3600; // hours to seconds
            s = strtok(NULL, ":");
        }
        if (s != NULL) {
            result += atoi(s) * 60; // minutes to seconds
            s = strtok(NULL, ":");
        }
        if (s != NULL) {
            result += atoi(s);
        }
    }
    else
    {
        result = atoi(value);
        if (result < 0 || result > 43199)
        {
            Serial.println(
                    "invalid value for " + name + ": " + HTTP.arg(name)
                    + " using 0 instead!");
            result = 0;
        }
    }
    return result;
}

uint8_t getValidDuration(String name)
{
  int i = HTTP.arg(name).toInt();
  if (i < 0 || i > 255)
  {
    Serial.println(
        "invalid value for " + name + ": " + HTTP.arg(name)
            + " using 32 instead!");
    i = 32;
  }
  return (uint8_t)i;
}

boolean getValidBoolean(String name)
{
  String value = HTTP.arg(name);
  return value.equalsIgnoreCase("true");
}

void handleAdjustment()
{
  uint16_t adj;
  if (HTTP.hasArg("set"))
  {
    if (HTTP.arg("set").equalsIgnoreCase("auto"))
    {
      Serial.println("Auto Adjust!");
      syncClockToRTC();
    }
    else
    {
      adj = getValidPosition("set");
      Serial.print("setting adjustment:");
      Serial.println(adj);
      setClockAdjustment(adj);
    }
  }

  adj = getClockAdjustment();

  HTTP.send(200, "text/plain", String(adj));
}

void handlePosition()
{
  uint16_t pos;
  if (HTTP.hasArg("set"))
  {
    pos = getValidPosition("set");
    Serial.print("setting position:");
    Serial.println(pos);
    setClockPosition(pos);
  }

  pos = getClockPosition();

  int hours = pos / 3600;
  int minutes = (pos - (hours*3600)) / 60;
  int seconds = pos - (hours*3600) - (minutes*60);
  char message[64];
  sprintf(message, "%d (%02d:%02d:%02d)\n", pos, hours, minutes, seconds);
  HTTP.send(200, "text/Plain", message);
}

void handleTPDuration()
{
  uint8_t value;
  if (HTTP.hasArg("set"))
  {
    value = getValidDuration("set");
    Serial.print("setting tp_duration:");
    Serial.println(value);
    setClockTPDuration(value);
  }

  value = getClockTPDuration();

  HTTP.send(200, "text/plain", String(value));
}

void handleAPDuration()
{
  uint8_t value;
  if (HTTP.hasArg("set"))
  {
    value = getValidDuration("set");
    Serial.print("setting ap_duration:");
    Serial.println(value);
    setClockAPDuration(value);
  }

  value = getClockAPDuration();

  HTTP.send(200, "text/plain", String(value));
}

void handleAPDelay()
{
  uint8_t value;
  if (HTTP.hasArg("set"))
  {
    value = getValidDuration("set");
    Serial.print("setting ap_delay:");
    Serial.println(value);
    setClockAPDelay(value);
  }

  value = getClockAPDelay();

  HTTP.send(200, "text/plain", String(value));
}

void handleEnable()
{
  boolean enable;
  if (HTTP.hasArg("set"))
  {
    enable = getValidBoolean("set");
    setClockEnable(enable);
  }
  enable = getClockEnable();
  HTTP.send(200, "text/Plain", String(enable));
}

void handleRTC()
{
  uint16_t value = getRTCTimeAsPosition();
  HTTP.send(200, "text/plain", String(value));
}

void handleNTP()
{
  Serial.println("disabling the clock!");
  setClockEnable(false);
  syncing = true;
  Serial.println("syncing now true!");
  HTTP.send(200, "text/Plain", "OK\n");
}

void setup()
{
  Serial.begin(115200);
  Serial.println("");
  Serial.println("Startup!");

  pinMode(SYNC_PIN, INPUT);

  rtc.Begin();
  RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);

  if (!rtc.IsDateTimeValid())
  {
    // Common Causes:
    //    1) first time you ran and the device wasn't running yet
    //    2) the battery on the device is low or even missing

    Serial.println("RTC lost confidence in the DateTime!");

    // following line sets the RTC to the date & time this sketch was compiled
    // it will also reset the valid flag internally unless the Rtc device is
    // having an issue

    rtc.SetDateTime(compiled);
  }

  if (!rtc.GetIsRunning())
  {
    Serial.println("RTC was not actively running, starting now");
    rtc.SetIsRunning(true);
  }

  // never assume the Rtc was last configured by you, so
  // just clear them to your needed state
#ifdef DS3231
  rtc.Enable32kHzPin(false);
#endif

  boolean enabled = getClockEnable();
  Serial.println("clock enable is:" + String(enabled));

  if (!enabled)
  {
    Serial.println("set mode none");
    rtc.SetSquareWavePin(SquareWavePin_ModeNone);
#ifdef DS3231
    Serial.println("set 1Hz");
    rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz);
#endif
  }
  else
  {
    Serial.println("clock is enabled, skipping init of RTC");
  }

  // setup wifi, blink let slow while connecting and fast if portal activated.
  feedback.blink(FEEDBACK_LED_SLOW);
  WiFiManager wifi;
  wifi.setAPCallback([](WiFiManager *)
  { feedback.blink(FEEDBACK_LED_FAST);});
  String ssid = "SynchroClock" + String(ESP.getChipId());
  wifi.autoConnect(ssid.c_str(), NULL);
  feedback.off();

  //Serial.println("set 1kHz");
  //rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1kHz);

  if (!enabled)
  {
//    Serial.println("setting clock position");
//    setClockPosition(0);
    Serial.println("enabling clock");
    setClockEnable(true);
    Serial.println("starting square wave");
#ifdef DS3231
    rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock);
#endif
#ifdef DS1307
    rtc.SetSquareWavePin(DS1307SquareWaveOut_1Hz);
#endif
  }
  else
  {
    Serial.println("clock is enabled, skipping init of clock!");
  }

  Serial.println("starting HTTP");
  HTTP.on("/adjust", HTTP_GET, handleAdjustment);
  HTTP.on("/position", HTTP_GET, handlePosition);
  HTTP.on("/tp_duration", HTTP_GET, handleTPDuration);
  HTTP.on("/ap_duration", HTTP_GET, handleAPDuration);
  HTTP.on("/ap_delay", HTTP_GET, handleAPDelay);
  HTTP.on("/enable", HTTP_GET, handleEnable);
  HTTP.on("/rtc", HTTP_GET, handleRTC);
  HTTP.on("/ntp", HTTP_GET, handleNTP);
  HTTP.begin();
  ntp.begin(1235);
  syncing = false;
  last_pin = 0;
}

void loop()
{
  HTTP.handleClient();
  uint8_t pin = digitalRead(SYNC_PIN);
  if (syncing && last_pin == 1 && pin == 0)
  {
    EpochTime start;
    start.seconds = rtc.GetDateTime().Epoch32Time();
    start.fraction = 0;
    EpochTime end = ntp.getTime(start);

    // compute the delay to the next second

    uint32_t msdelay = 1000 - (((uint64_t) end.fraction * 1000) >> 32);
    Serial.print("msdelay: ");
    Serial.println(msdelay);
    // wait for the next second
    if (msdelay > 0 && msdelay < 1000)
    {
      delay(msdelay);
    }
    RtcDateTime dt(end.seconds + 1); // +1 because we waited for the next second
    rtc.SetDateTime(dt);
    syncing = false;
    last_pin = 0;
    delay(500);
    Serial.println("rtc updated, starting clock!");
    setClockEnable(true);
    Serial.println("syncing clock to RTC");
    syncClockToRTC();
  }
  else
  {
    last_pin = pin;
  }
}

void syncClockToRTC()
{
  uint16_t rtc_pos = getRTCTimeAsPosition();
  Serial.printf("RTC position:%d", rtc_pos);
  uint16_t clock_pos = getClockPosition();
  Serial.printf("clock position:%d", clock_pos);
  if (clock_pos != rtc_pos)
  {
    int adj = rtc_pos - clock_pos;
    if (adj < 0)
    {
      adj += 43200;
    }
    Serial.printf("sending adjustment of %d\n", adj);
    setClockAdjustment(adj);
  }
}

//
// RTC functions
//
uint16_t getRTCTimeAsPosition()
{
  RtcDateTime time = rtc.GetDateTime();
  uint16_t hour = time.Hour();
  uint16_t minute = time.Minute();
  uint16_t second = time.Second();
  if (hour > 11)
  {
    hour -= 12;
    Serial.printf("%02d:%02d:%02d (CORRECTED)\n", hour, minute, second);
  }
  else
  {
    Serial.printf("%02d:%02d:%02d\n", hour, minute, second);
  }
  uint16_t position = hour * 60 * 60 + minute * 60 + second;
  return position;
}

#define countof(a) (sizeof(a) / sizeof(a[0]))

void printDateTime(const RtcDateTime& dt)
{
  char datestring[20];

  snprintf_P(datestring, countof(datestring),
      PSTR("%02u/%02u/%04u %02u:%02u:%02u"), dt.Month(), dt.Day(), dt.Year(),
      dt.Hour(), dt.Minute(), dt.Second());
  Serial.print(datestring);
}

//
// access to analog clock controler via i2c
//
uint16_t getClockAdjustment()
{
  return readClock16(CMD_ADJUSTMENT);
}

void setClockAdjustment(uint16_t value)
{
  writeClock16(CMD_ADJUSTMENT, value);
}

uint16_t getClockPosition()
{
  return readClock16(CMD_POSITION);
}

void setClockPosition(uint16_t value)
{
  writeClock16(CMD_POSITION, value);
}

uint8_t getClockTPDuration()
{
  return readClock8(CMD_TP_DURATION);
}

void setClockTPDuration(uint8_t value)
{
  writeClock8(CMD_TP_DURATION, value);
}

uint8_t getClockAPDuration()
{
  return readClock8(CMD_AP_DURATION);
}

void setClockAPDuration(uint8_t value)
{
  writeClock8(CMD_AP_DURATION, value);
}

uint8_t getClockAPDelay()
{
  return readClock8(CMD_AP_DELAY);
}

void setClockAPDelay(uint8_t value)
{
  writeClock8(CMD_AP_DELAY, value);
}

boolean getClockEnable()
{
  Wire.beginTransmission((uint8_t) I2C_ADDRESS);
  Wire.write(CMD_CONTROL);
  Wire.endTransmission();
  Wire.requestFrom((uint8_t) I2C_ADDRESS, (uint8_t) 1);
  uint8_t value = Wire.read();
  return ((value & BIT_ENABLE) == BIT_ENABLE);
}

void setClockEnable(boolean enable)
{
  Wire.beginTransmission(I2C_ADDRESS);
  Wire.write(CMD_CONTROL);
  Wire.endTransmission();

  Wire.requestFrom((uint8_t) I2C_ADDRESS, (uint8_t) 1);
  uint8_t value = Wire.read();

  if (enable)
  {
    value |= BIT_ENABLE;
  }
  else
  {
    value &= ~BIT_ENABLE;
  }

  Wire.beginTransmission(I2C_ADDRESS);
  Wire.write(CMD_CONTROL);
  Wire.write(value);
  Wire.endTransmission();
}

// send a command, read a 16 bit value
uint16_t readClock16(uint8_t command)
{
  Wire.beginTransmission(I2C_ADDRESS);
  Wire.write(command);
  Wire.endTransmission();
  uint16_t value;
  Wire.requestFrom(I2C_ADDRESS, sizeof(value));
  Wire.readBytes((uint8_t*) &value, sizeof(value));
  return (value);
}

// send a command with a 16 bit value
void writeClock16(uint8_t command, uint16_t value)
{
  Wire.beginTransmission(I2C_ADDRESS);
  Wire.write(command);
  Wire.write((uint8_t*) &value, sizeof(value));
  Wire.endTransmission();
}

// send a command, read a 8 bit value
uint8_t readClock8(uint8_t command)
{
  Wire.beginTransmission(I2C_ADDRESS);
  Wire.write(command);
  Wire.endTransmission();
  uint8_t value;
  Wire.requestFrom(I2C_ADDRESS, sizeof(value));
  Wire.readBytes((uint8_t*) &value, sizeof(value));
  return (value);
}

// send a command with a 16 bit value
void writeClock8(uint8_t command, uint8_t value)
{
  Wire.beginTransmission(I2C_ADDRESS);
  Wire.write(command);
  Wire.write(value);
  Wire.endTransmission();
}

<|endoftext|>