<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id: ValuePrinter.cpp 46307 2012-10-04 06:53:23Z axel $
// author:  Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------

#include "cling/Interpreter/StoredValueRef.h"
#include "clang/AST/ASTContext.h"

using namespace cling;
using namespace clang;
using namespace llvm;

StoredValueRef::StoredValue::StoredValue(const ASTContext& ctx, QualType t):
m_Mem(0) {
  type = t;
  if (!(t->isIntegralOrEnumerationType()
        || t->isRealFloatingType()
        || t->hasPointerRepresentation())) {
    const int64_t size = getAllocSizeInBytes(ctx);
    if (size > sizeof(m_Buf))
      m_Mem = new char[size];
    else m_Mem = m_Buf;
    value = llvm::PTOGV(m_Mem);
  };
}

StoredValueRef::StoredValue::~StoredValue() {
  if (m_Mem != m_Buf)
    delete [] m_Mem;
}

int64_t StoredValueRef::StoredValue::getAllocSizeInBytes(
                                                  const ASTContext& ctx) const {
  return ctx.getTypeSizeInChars(type).getQuantity();
}

StoredValueRef StoredValueRef::allocate(const ASTContext& ctx, QualType t) {
  return new StoredValue(ctx, t);
}

StoredValueRef StoredValueRef::bitwiseCopy(const ASTContext& ctx,
                                           const Value& value) {
  StoredValue* SValue = new StoredValue(ctx, value.type);
  if (SValue->m_Mem) {
    const char* src = (const char*)value.value.PointerVal;
    // It's not a pointer. LLVM stores a char[5] (i.e. 5 x i8) as an i40,
    // so use that instead. We don't keep it as an int; instead, we "allocate"
    // it as a "proper" char[5] in the m_Mem. "Allocate" because it uses the
    // m_Buf, so no actual allocation happens.
    uint64_t IntVal = value.value.IntVal.getSExtValue();
    if (!src) src = (const char*)&IntVal;
    memcpy(SValue->m_Mem, src,
           SValue->getAllocSizeInBytes(ctx));
  } else {
    SValue->value = value.value;
  }
  return SValue;
}

<commit_msg>Fix warning (un/signed comparison)<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id: ValuePrinter.cpp 46307 2012-10-04 06:53:23Z axel $
// author:  Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------

#include "cling/Interpreter/StoredValueRef.h"
#include "clang/AST/ASTContext.h"

using namespace cling;
using namespace clang;
using namespace llvm;

StoredValueRef::StoredValue::StoredValue(const ASTContext& ctx, QualType t):
m_Mem(0) {
  type = t;
  if (!(t->isIntegralOrEnumerationType()
        || t->isRealFloatingType()
        || t->hasPointerRepresentation())) {
    const uint64_t size = (uint64_t)getAllocSizeInBytes(ctx);
    if (size > sizeof(m_Buf))
      m_Mem = new char[size];
    else m_Mem = m_Buf;
    value = llvm::PTOGV(m_Mem);
  };
}

StoredValueRef::StoredValue::~StoredValue() {
  if (m_Mem != m_Buf)
    delete [] m_Mem;
}

int64_t StoredValueRef::StoredValue::getAllocSizeInBytes(
                                                  const ASTContext& ctx) const {
  return ctx.getTypeSizeInChars(type).getQuantity();
}

StoredValueRef StoredValueRef::allocate(const ASTContext& ctx, QualType t) {
  return new StoredValue(ctx, t);
}

StoredValueRef StoredValueRef::bitwiseCopy(const ASTContext& ctx,
                                           const Value& value) {
  StoredValue* SValue = new StoredValue(ctx, value.type);
  if (SValue->m_Mem) {
    const char* src = (const char*)value.value.PointerVal;
    // It's not a pointer. LLVM stores a char[5] (i.e. 5 x i8) as an i40,
    // so use that instead. We don't keep it as an int; instead, we "allocate"
    // it as a "proper" char[5] in the m_Mem. "Allocate" because it uses the
    // m_Buf, so no actual allocation happens.
    uint64_t IntVal = value.value.IntVal.getSExtValue();
    if (!src) src = (const char*)&IntVal;
    memcpy(SValue->m_Mem, src,
           SValue->getAllocSizeInBytes(ctx));
  } else {
    SValue->value = value.value;
  }
  return SValue;
}

<|endoftext|>


<commit_before>#ifndef IS_NUMBER_POWER_OF_TWO_HPP
#define IS_NUMBER_POWER_OF_TWO_HPP

// Check if number if power of two

// How we represent in bits number that is power of two:
// 2 - 0b10
// 4 - 0b100
// 8 - 0b100
// 16 - 0b1000
// .......
// All these numbers have only one bit set to '1'.

// How we represent int bits number that is equals to
// (number_of_power_of_two - 1):
// 1 - 0b1
// 3 - 0b11
// 7 - 0b111
// 15 - 0b1111
// .......

// Result of operation x & (x - 1) where x is a number of power of two:
// x = 4
// 4 & (4 - 1) = 4 & 3 =
// 0b100 & 0b011 =
// 1 & 0 = 0, 0 & 1 = 0, 0 & 1 = 0 -> 0b0 = 0

// Result of operation x & (x - 1) where x IS NOT a number of power of two:
// x = 6
// 6 & (6 - 1) = 6 & 5 =
// 0b110 & 0b101 =
// 1 & 1 = 1, 1 & 0 = 0, 0 & 1 = 0 -> 0b100 = 4

namespace NumberPowerOfTwo
{

class Solution
{
public:
    bool isPowerOfTwo(const int& number)
    {
        bool result = (number & (number - 1)) == 0;
        return result;
    }
};

}

#endif // IS_NUMBER_POWER_OF_TWO_HPP
<commit_msg>Fix code that check if number if power of two<commit_after>#ifndef IS_NUMBER_POWER_OF_TWO_HPP
#define IS_NUMBER_POWER_OF_TWO_HPP

// https://leetcode.com/problems/power-of-two/description/
// Check if number if power of two

// How we represent in bits number that is power of two:
// 2 - 0b10
// 4 - 0b100
// 8 - 0b100
// 16 - 0b1000
// .......
// All these numbers have only one bit set to '1'.

// How we represent int bits number that is equals to
// (number_of_power_of_two - 1):
// 1 - 0b1
// 3 - 0b11
// 7 - 0b111
// 15 - 0b1111
// .......

// Result of operation x & (x - 1) where x is a number of power of two:
// x = 4
// 4 & (4 - 1) = 4 & 3 =
// 0b100 & 0b011 =
// 1 & 0 = 0, 0 & 1 = 0, 0 & 1 = 0 -> 0b0 = 0

// Result of operation x & (x - 1) where x IS NOT a number of power of two:
// x = 6
// 6 & (6 - 1) = 6 & 5 =
// 0b110 & 0b101 =
// 1 & 1 = 1, 1 & 0 = 0, 0 & 1 = 0 -> 0b100 = 4

namespace NumberPowerOfTwo {

class Solution {
public:
    bool isPowerOfTwo(const int& number) {
        if (n <= 0) {
            return false;
        }

        bool result = (number & (number - 1)) == 0;
        return result;
    }
};

}

#endif // IS_NUMBER_POWER_OF_TWO_HPP
<|endoftext|>


<commit_before>// Copyright 2018 Alexander Gallego
//

#include <cstring>
#include <memory>

#include <benchmark/benchmark.h>
#include <fmt/format.h>

#include "hashing/hashing_utils.h"

using namespace smf; // NOLINT

static uint64_t
stack_hash(const std::string &topic, uint32_t partition) {
  const std::size_t kBufSize = topic.size() + 4;
  char              buf[kBufSize];
  std::memset(buf, 0, kBufSize);
  std::memcpy(buf, (char *)&partition, 4);
  std::memcpy(buf + 4, topic.c_str(), topic.size());
  return xxhash_64(buf, kBufSize);
}

static uint64_t
dynamic_hash(const std::string &topic, uint32_t partition) {
  fmt::MemoryWriter w;
  w.write("{}:{}", topic, partition);
  return xxhash_64(w.data(), w.size());
}
static uint64_t
incremental_hash(const std::string &topic, uint32_t partition) {
  incremental_xxhash64 inc;
  inc.update((char *)&partition, sizeof(partition));
  inc.update(topic.data(), topic.size());
  return inc.digest();
}

static void
BM_stackhash(benchmark::State &state) {
  for (auto _ : state) {
    state.PauseTiming();
    std::string x;
    x.reserve(state.range(0));
    for (auto i = 0u; i < state.range(0); ++i) { x.push_back('x'); }
    state.ResumeTiming();
    benchmark::DoNotOptimize(stack_hash(x, state.range(0)));
  }
}
BENCHMARK(BM_stackhash)
  ->Args({1 << 1, 1 << 1})
  ->Args({1 << 4, 1 << 4})
  ->Args({1 << 8, 1 << 8})
  ->Args({1 << 16, 1 << 16});

static void
BM_dynamichash(benchmark::State &state) {
  for (auto _ : state) {
    state.PauseTiming();
    std::string x;
    x.reserve(state.range(0));
    for (auto i = 0u; i < state.range(0); ++i) { x.push_back('x'); }
    state.ResumeTiming();
    benchmark::DoNotOptimize(dynamic_hash(x, state.range(0)));
  }
}
BENCHMARK(BM_dynamichash)
  ->Args({1 << 1, 1 << 1})
  ->Args({1 << 4, 1 << 4})
  ->Args({1 << 8, 1 << 8})
  ->Args({1 << 16, 1 << 16});

static void
BM_incremental_hash(benchmark::State &state) {
  for (auto _ : state) {
    state.PauseTiming();
    std::string x;
    x.reserve(state.range(0));
    for (auto i = 0u; i < state.range(0); ++i) { x.push_back('x'); }
    state.ResumeTiming();
    benchmark::DoNotOptimize(incremental_hash(x, state.range(0)));
  }
}
BENCHMARK(BM_incremental_hash)
  ->Args({1 << 1, 1 << 1})
  ->Args({1 << 4, 1 << 4})
  ->Args({1 << 8, 1 << 8})
  ->Args({1 << 16, 1 << 16});

BENCHMARK_MAIN();
<commit_msg>benchmarking: Add benchmarking of fixed array<commit_after>// Copyright 2018 Alexander Gallego
//

#include <cstring>
#include <memory>

#include <benchmark/benchmark.h>
#include <fmt/format.h>

#include "hashing/hashing_utils.h"

using namespace smf;  // NOLINT

static uint64_t
fixed_hash(const std::string &topic, uint32_t partition) {
  const std::size_t kBufSize = 50;
  char              buf[kBufSize];
  std::memset(buf, 0, kBufSize);
  std::memcpy(buf, (char *)&partition, 4);
  std::memcpy(buf + 4, topic.c_str(), std::min(kBufSize - 4, topic.size()));
  return xxhash_64(buf, kBufSize);
}

static uint64_t
stack_hash(const std::string &topic, uint32_t partition) {
  const std::size_t kBufSize = topic.size() + 4;
  char              buf[kBufSize];
  std::memset(buf, 0, kBufSize);
  std::memcpy(buf, (char *)&partition, 4);
  std::memcpy(buf + 4, topic.c_str(), topic.size());
  return xxhash_64(buf, kBufSize);
}

static uint64_t
dynamic_hash(const std::string &topic, uint32_t partition) {
  fmt::MemoryWriter w;
  w.write("{}:{}", topic, partition);
  return xxhash_64(w.data(), w.size());
}
static uint64_t
incremental_hash(const std::string &topic, uint32_t partition) {
  incremental_xxhash64 inc;
  inc.update((char *)&partition, sizeof(partition));
  inc.update(topic.data(), topic.size());
  return inc.digest();
}

static void
BM_fixed_hash(benchmark::State &state) {
  for (auto _ : state) {
    state.PauseTiming();
    std::string x;
    x.reserve(state.range(0));
    for (auto i = 0u; i < state.range(0); ++i) { x.push_back('x'); }
    state.ResumeTiming();
    benchmark::DoNotOptimize(fixed_hash(x, state.range(0)));
  }
}
BENCHMARK(BM_fixed_hash)
->Args({1 << 1, 1 << 1})
->Args({1 << 4, 1 << 4})
->Args({1 << 8, 1 << 8})
->Args({1 << 16, 1 << 16});

static void
BM_stackhash(benchmark::State &state) {
  for (auto _ : state) {
    state.PauseTiming();
    std::string x;
    x.reserve(state.range(0));
    for (auto i = 0u; i < state.range(0); ++i) { x.push_back('x'); }
    state.ResumeTiming();
    benchmark::DoNotOptimize(stack_hash(x, state.range(0)));
  }
}
BENCHMARK(BM_stackhash)
  ->Args({1 << 1, 1 << 1})
  ->Args({1 << 4, 1 << 4})
  ->Args({1 << 8, 1 << 8})
  ->Args({1 << 16, 1 << 16});

static void
BM_dynamichash(benchmark::State &state) {
  for (auto _ : state) {
    state.PauseTiming();
    std::string x;
    x.reserve(state.range(0));
    for (auto i = 0u; i < state.range(0); ++i) { x.push_back('x'); }
    state.ResumeTiming();
    benchmark::DoNotOptimize(dynamic_hash(x, state.range(0)));
  }
}
BENCHMARK(BM_dynamichash)
  ->Args({1 << 1, 1 << 1})
  ->Args({1 << 4, 1 << 4})
  ->Args({1 << 8, 1 << 8})
  ->Args({1 << 16, 1 << 16});

static void
BM_incremental_hash(benchmark::State &state) {
  for (auto _ : state) {
    state.PauseTiming();
    std::string x;
    x.reserve(state.range(0));
    for (auto i = 0u; i < state.range(0); ++i) { x.push_back('x'); }
    state.ResumeTiming();
    benchmark::DoNotOptimize(incremental_hash(x, state.range(0)));
  }
}
BENCHMARK(BM_incremental_hash)
  ->Args({1 << 1, 1 << 1})
  ->Args({1 << 4, 1 << 4})
  ->Args({1 << 8, 1 << 8})
  ->Args({1 << 16, 1 << 16});

BENCHMARK_MAIN();
<|endoftext|>


<commit_before>/***************************************************************
 *
 * Copyright (C) 1990-2013, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>

// When running with PID namespaces enabled (via the
// USE_PID_NAMESPACES = true
// flag, the process that the starter forks (usually the job) runs
// with pid 1, at least to processes within the namespace.  PID 1
// has special responsibilities.  First, all signals sent to it
// (except SIGKILL, SIGSTOP and SIGCONT) are blocked if there is
// no handler installed.  So, if we just ran the job as pid 1,
// the softkill signal (SIGQUIT) isn't delivered, and after a condor_rm
// the job continues runnning until a hardkill.
//
// Also, the pid 1 acts like the real init wrt reaping orphan processes.
// If a child of this process forks a grandchild, and the child dies, the
// grandchild is reparented to this pid 1, not the ur-pid 1 launched by
// the kernel at boot time.  Thus we need to be careful that we must
// continue to reap processes that may not be direct children of this
// process.  If we don't, they will get re-parented up to the real init
// upon the death of this processes, but I think we lose resource
// information from when the starter calls getrusage on this process.

// To fix these problems, instead of running the job as pid 1, we run
// this simple init like process instead, which turns around and forks
// and execs the job proper.  It registers signal handlers, so that the
// starter can softkill and send other signals to it.  These signals are generally intended for the job, so they are forwarded to the job itself.  That way
// if the job catches SIGQUIT, it can itself perform a graceful shutdown.
//
// If the job itself exits with a signal, we need a way to communicate that back
// up to the starter.  This is done via a file, which is written by this
// process after the job exits.  The starter parses this file to get the
// real exit status and whether the job exitted via a signal.

pid_t child_pid = -1;

// If we receive a signal from the outside world, it was probably intended for our
// child.  Pass it down to the child.
void signal_handler(int signo) {
	if (child_pid > 0) {
		kill(child_pid, signo);
	}
}

void write_status(const char *message, int status) {
	const char *filename = getenv("_CONDOR_PID_NS_INIT_STATUS_FILENAME");
	if (filename) {
		FILE *f = fopen(filename, "w");
		if (f) {
			fprintf(f, "%s: %d\n", message, status);
			fclose(f);
		}
	}
}

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

	struct sigaction sa;
	sa.sa_handler = signal_handler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;

	// Remove the status file, if it exists
	unlink(getenv("_CONDOR_PID_NS_INIT_STATUS_FILENAME"));

	// Install the signal handlers, so if we're running as pid 1 they will
	// get delivered.

	sigaction(SIGHUP, &sa, 0);
	sigaction(SIGINT, &sa, 0);
	sigaction(SIGQUIT, &sa, 0);
	sigaction(SIGILL, &sa, 0);
	sigaction(SIGABRT, &sa, 0);
	sigaction(SIGTERM, &sa, 0);
	sigaction(SIGSEGV, &sa, 0);
	sigaction(SIGPIPE, &sa, 0);
	sigaction(SIGUSR1, &sa, 0);
	sigaction(SIGUSR2, &sa, 0);

	child_pid = fork();

	if (child_pid == -1) {
		/// fork failed
		// Write nothing to log file, starter knows this means something failed
		// with this tool itself.  There's nothing wrong with the job.
		exit(-1);
	}

	if (child_pid == 0) {
		// in the child
		int i = 0;
		for (i = 0; i < argc - 1; i++) {
			argv[i] = argv[i + 1];
		}
		argv[i] = 0;
		argc--;
		execve(argv[0], argv, envp);

		// We're in trouble if we get here... exec failed
		// maybe bad path, maybe bad interpreter...
		// Must let starter and shadow know that something is wrong with this
		// job, not the machine.
		write_status("ExecFailed", errno);
		exit(0);
	}

	if (child_pid > 0) {
		// in the parent, just wait to see what happens...
		int status;

retry:
		int result = wait(&status);
		if (((result == -1) && (errno == EINTR)) || (result != child_pid)) {
			goto retry; // Signal must have been handled
		}

		// Child has exited.  First see if we failed to exec
		struct stat buf;
		result = stat(getenv("_CONDOR_PID_NS_INIT_STATUS_FILENAME"), &buf);
		if (result != 0) {
			write_status("Exited", status);
		}
		exit(WEXITSTATUS(status));
	}
}
<commit_msg>Calm coverity #6992<commit_after>/***************************************************************
 *
 * Copyright (C) 1990-2013, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>

// When running with PID namespaces enabled (via the
// USE_PID_NAMESPACES = true
// flag, the process that the starter forks (usually the job) runs
// with pid 1, at least to processes within the namespace.  PID 1
// has special responsibilities.  First, all signals sent to it
// (except SIGKILL, SIGSTOP and SIGCONT) are blocked if there is
// no handler installed.  So, if we just ran the job as pid 1,
// the softkill signal (SIGQUIT) isn't delivered, and after a condor_rm
// the job continues runnning until a hardkill.
//
// Also, the pid 1 acts like the real init wrt reaping orphan processes.
// If a child of this process forks a grandchild, and the child dies, the
// grandchild is reparented to this pid 1, not the ur-pid 1 launched by
// the kernel at boot time.  Thus we need to be careful that we must
// continue to reap processes that may not be direct children of this
// process.  If we don't, they will get re-parented up to the real init
// upon the death of this processes, but I think we lose resource
// information from when the starter calls getrusage on this process.

// To fix these problems, instead of running the job as pid 1, we run
// this simple init like process instead, which turns around and forks
// and execs the job proper.  It registers signal handlers, so that the
// starter can softkill and send other signals to it.  These signals are generally intended for the job, so they are forwarded to the job itself.  That way
// if the job catches SIGQUIT, it can itself perform a graceful shutdown.
//
// If the job itself exits with a signal, we need a way to communicate that back
// up to the starter.  This is done via a file, which is written by this
// process after the job exits.  The starter parses this file to get the
// real exit status and whether the job exitted via a signal.

pid_t child_pid = -1;

// If we receive a signal from the outside world, it was probably intended for our
// child.  Pass it down to the child.
void signal_handler(int signo) {
	if (child_pid > 0) {
		kill(child_pid, signo);
	}
}

void write_status(const char *message, int status) {
	const char *filename = getenv("_CONDOR_PID_NS_INIT_STATUS_FILENAME");
	if (filename) {
		FILE *f = fopen(filename, "w");
		if (f) {
			fprintf(f, "%s: %d\n", message, status);
			fclose(f);
		}
	}
}

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

	struct sigaction sa;
	sa.sa_handler = signal_handler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;

	// Remove the status file, if it exists
	unlink(getenv("_CONDOR_PID_NS_INIT_STATUS_FILENAME"));

	// Install the signal handlers, so if we're running as pid 1 they will
	// get delivered.

	sigaction(SIGHUP, &sa, 0);
	sigaction(SIGINT, &sa, 0);
	sigaction(SIGQUIT, &sa, 0);
	sigaction(SIGILL, &sa, 0);
	sigaction(SIGABRT, &sa, 0);
	sigaction(SIGTERM, &sa, 0);
	sigaction(SIGSEGV, &sa, 0);
	sigaction(SIGPIPE, &sa, 0);
	sigaction(SIGUSR1, &sa, 0);
	sigaction(SIGUSR2, &sa, 0);

	child_pid = fork();

	if (child_pid == -1) {
		/// fork failed
		// Write nothing to log file, starter knows this means something failed
		// with this tool itself.  There's nothing wrong with the job.
		exit(-1);
	}

	if (child_pid == 0) {
		// in the child
		int i = 0;
		for (i = 0; i < argc - 1; i++) {
			argv[i] = argv[i + 1];
		}
		argv[i] = 0;
		argc--;
		execve(argv[0], argv, envp);

		// We're in trouble if we get here... exec failed
		// maybe bad path, maybe bad interpreter...
		// Must let starter and shadow know that something is wrong with this
		// job, not the machine.
		write_status("ExecFailed", errno);
		exit(0);
	}

	if (child_pid > 0) {
		// in the parent, just wait to see what happens...
		int status;

retry:
		int result = wait(&status);
		if (((result == -1) && (errno == EINTR)) || (result != child_pid)) {
			goto retry; // Signal must have been handled
		}

		// Child has exited.  First see if we failed to exec
		struct stat buf;
		const char *filename = getenv("_CONDOR_PID_NS_INIT_STATUS_FILENAME");
		if (filename) {
			result = stat(filename, &buf);
			if (result != 0) {
				write_status("Exited", status);
			}
		}
		exit(WEXITSTATUS(status));
	}
}
<|endoftext|>


<commit_before>#include "CommandController.hpp"

#include "Application.hpp"
#include "common/SignalVector.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/commands/Command.hpp"
#include "controllers/commands/CommandModel.hpp"
#include "messages/MessageBuilder.hpp"
#include "providers/twitch/TwitchChannel.hpp"
#include "providers/twitch/TwitchServer.hpp"
#include "singletons/Paths.hpp"
#include "singletons/Settings.hpp"

#include <QApplication>
#include <QFile>
#include <QRegularExpression>

#define TWITCH_DEFAULT_COMMANDS                                                               \
    {                                                                                \
        "/help", "/w", "/me", "/disconnect", "/mods", "/color", "/ban", "/unban", "/timeout", \
            "/untimeout", "/slow", "/slowoff", "/r9kbeta", "/r9kbetaoff", "/emoteonly",       \
            "/emoteonlyoff", "/clear", "/subscribers", "/subscribersoff", "/followers",       \
            "/followersoff"                                                                   \
    }

namespace chatterino {

CommandController::CommandController()
{
    auto addFirstMatchToMap = [this](auto args) {
        this->commandsMap_.remove(args.item.name);

        for (const Command &cmd : this->items.getVector()) {
            if (cmd.name == args.item.name) {
                this->commandsMap_[cmd.name] = cmd;
                break;
            }
        }
    };

    this->items.itemInserted.connect(addFirstMatchToMap);
    this->items.itemRemoved.connect(addFirstMatchToMap);
}

void CommandController::load()
{
    auto app = getApp();
    this->filePath_ = app->paths->settingsDirectory + "/commands.txt";

    QFile textFile(this->filePath_);
    if (!textFile.open(QIODevice::ReadOnly)) {
        // No commands file created yet
        return;
    }

    QList<QByteArray> test = textFile.readAll().split('\n');

    for (const auto &command : test) {
        if (command.isEmpty()) {
            continue;
        }

        this->items.appendItem(Command(command));
    }

    textFile.close();
}

void CommandController::save()
{
    QFile textFile(this->filePath_);
    if (!textFile.open(QIODevice::WriteOnly)) {
        Log("[CommandController::saveCommands] Unable to open {} for writing", this->filePath_);
        return;
    }

    for (const Command &cmd : this->items.getVector()) {
        textFile.write((cmd.toString() + "\n").toUtf8());
    }

    textFile.close();
}

CommandModel *CommandController::createModel(QObject *parent)
{
    CommandModel *model = new CommandModel(parent);
    model->init(&this->items);

    return model;
}

QString CommandController::execCommand(const QString &text, ChannelPtr channel, bool dryRun)
{
    QStringList words = text.split(' ', QString::SkipEmptyParts);
    Command command;

    {
        std::lock_guard<std::mutex> lock(this->mutex_);

        if (words.length() == 0) {
            return text;
        }

        QString commandName = words[0];

        // works in a valid twitch channel and /whispers, etc...
        if (!dryRun && channel->isTwitchChannel()) {
            if (commandName == "/w") {
                if (words.length() <= 2) {
                    return "";
                }

                auto app = getApp();

                MessageBuilder b;

                b.emplace<TimestampElement>();
                b.emplace<TextElement>(app->accounts->twitch.getCurrent()->getUserName(),
                                       MessageElement::Text, MessageColor::Text,
                                       FontStyle::ChatMediumBold);
                b.emplace<TextElement>("->", MessageElement::Text);
                b.emplace<TextElement>(words[1] + ":", MessageElement::Text, MessageColor::Text,
                                       FontStyle::ChatMediumBold);

                QString rest = "";

                for (int i = 2; i < words.length(); i++) {
                    rest += words[i] + " ";
                }

                b.emplace<TextElement>(rest, MessageElement::Text);
                b.getMessage()->flags |= Message::DoNotTriggerNotification;

                app->twitch.server->whispersChannel->addMessage(b.getMessage());

                app->twitch.server->getWriteConnection()->sendRaw("PRIVMSG #jtv :" + text + "\r\n");

                if (app->settings->inlineWhispers) {
                    app->twitch.server->forEachChannel(
                        [&b](ChannelPtr _channel) { _channel->addMessage(b.getMessage()); });
                }

                return "";
            }
        }

        // check if default command exists
        auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());

        // works only in a valid twitch channel
        if (!dryRun && twitchChannel != nullptr) {
            if (commandName == "/debug-args") {
                QString msg = QApplication::instance()->arguments().join(' ');

                channel->addMessage(Message::createSystemMessage(msg));

                return "";
            } else if (commandName == "/uptime") {
                const auto &streamStatus = twitchChannel->getStreamStatus();

                QString messageText =
                    streamStatus.live ? streamStatus.uptime : "Channel is not live.";

                channel->addMessage(Message::createSystemMessage(messageText));

                return "";
            } else if (commandName == "/ignore" && words.size() >= 2) {
                auto app = getApp();

                auto user = app->accounts->twitch.getCurrent();
                auto target = words.at(1);

                if (user->isAnon()) {
                    channel->addMessage(
                        Message::createSystemMessage("You must be logged in to ignore someone"));
                    return "";
                }

                user->ignore(target, [channel](auto resultCode, const QString &message) {
                    channel->addMessage(Message::createSystemMessage(message));
                });

                return "";
            } else if (commandName == "/unignore" && words.size() >= 2) {
                auto app = getApp();

                auto user = app->accounts->twitch.getCurrent();
                auto target = words.at(1);

                if (user->isAnon()) {
                    channel->addMessage(
                        Message::createSystemMessage("You must be logged in to ignore someone"));
                    return "";
                }

                user->unignore(target, [channel](auto resultCode, const QString &message) {
                    channel->addMessage(Message::createSystemMessage(message));
                });

                return "";
            }
        }

        // check if custom command exists
        auto it = this->commandsMap_.find(commandName);
        if (it == this->commandsMap_.end()) {
            return text;
        }

        command = it.value();
    }

    return this->execCustomCommand(words, command);
}

QString CommandController::execCustomCommand(const QStringList &words, const Command &command)
{
    QString result;

    static QRegularExpression parseCommand("(^|[^{])({{)*{(\\d+\\+?)}");

    int lastCaptureEnd = 0;

    auto globalMatch = parseCommand.globalMatch(command.func);
    int matchOffset = 0;

    while (true) {
        QRegularExpressionMatch match = parseCommand.match(command.func, matchOffset);

        if (!match.hasMatch()) {
            break;
        }

        result += command.func.mid(lastCaptureEnd, match.capturedStart() - lastCaptureEnd + 1);

        lastCaptureEnd = match.capturedEnd();
        matchOffset = lastCaptureEnd - 1;

        QString wordIndexMatch = match.captured(3);

        bool plus = wordIndexMatch.at(wordIndexMatch.size() - 1) == '+';
        wordIndexMatch = wordIndexMatch.replace("+", "");

        bool ok;
        int wordIndex = wordIndexMatch.replace("=", "").toInt(&ok);
        if (!ok || wordIndex == 0) {
            result += "{" + match.captured(3) + "}";
            continue;
        }

        if (words.length() <= wordIndex) {
            continue;
        }

        if (plus) {
            bool first = true;
            for (int i = wordIndex; i < words.length(); i++) {
                if (!first) {
                    result += " ";
                }
                result += words[i];
                first = false;
            }
        } else {
            result += words[wordIndex];
        }
    }

    result += command.func.mid(lastCaptureEnd);

    if (result.size() > 0 && result.at(0) == '{') {
        result = result.mid(1);
    }

    return result.replace("{{", "{");
}

QStringList CommandController::getDefaultTwitchCommandList()
{
    QStringList l = TWITCH_DEFAULT_COMMANDS;
    l += "/uptime";

    return l;
}

}  // namespace chatterino
<commit_msg>Add text command to display logs (#574)<commit_after>#include "CommandController.hpp"

#include "Application.hpp"
#include "common/SignalVector.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/commands/Command.hpp"
#include "controllers/commands/CommandModel.hpp"
#include "messages/MessageBuilder.hpp"
#include "providers/twitch/TwitchChannel.hpp"
#include "providers/twitch/TwitchServer.hpp"
#include "singletons/Paths.hpp"
#include "singletons/Settings.hpp"
#include "widgets/dialogs/LogsPopup.hpp"

#include <QApplication>
#include <QFile>
#include <QRegularExpression>

#define TWITCH_DEFAULT_COMMANDS                                                               \
    {                                                                                \
        "/help", "/w", "/me", "/disconnect", "/mods", "/color", "/ban", "/unban", "/timeout", \
            "/untimeout", "/slow", "/slowoff", "/r9kbeta", "/r9kbetaoff", "/emoteonly",       \
            "/emoteonlyoff", "/clear", "/subscribers", "/subscribersoff", "/followers",       \
            "/followersoff"                                                                   \
    }

namespace chatterino {

CommandController::CommandController()
{
    auto addFirstMatchToMap = [this](auto args) {
        this->commandsMap_.remove(args.item.name);

        for (const Command &cmd : this->items.getVector()) {
            if (cmd.name == args.item.name) {
                this->commandsMap_[cmd.name] = cmd;
                break;
            }
        }
    };

    this->items.itemInserted.connect(addFirstMatchToMap);
    this->items.itemRemoved.connect(addFirstMatchToMap);
}

void CommandController::load()
{
    auto app = getApp();
    this->filePath_ = app->paths->settingsDirectory + "/commands.txt";

    QFile textFile(this->filePath_);
    if (!textFile.open(QIODevice::ReadOnly)) {
        // No commands file created yet
        return;
    }

    QList<QByteArray> test = textFile.readAll().split('\n');

    for (const auto &command : test) {
        if (command.isEmpty()) {
            continue;
        }

        this->items.appendItem(Command(command));
    }

    textFile.close();
}

void CommandController::save()
{
    QFile textFile(this->filePath_);
    if (!textFile.open(QIODevice::WriteOnly)) {
        Log("[CommandController::saveCommands] Unable to open {} for writing", this->filePath_);
        return;
    }

    for (const Command &cmd : this->items.getVector()) {
        textFile.write((cmd.toString() + "\n").toUtf8());
    }

    textFile.close();
}

CommandModel *CommandController::createModel(QObject *parent)
{
    CommandModel *model = new CommandModel(parent);
    model->init(&this->items);

    return model;
}

QString CommandController::execCommand(const QString &text, ChannelPtr channel, bool dryRun)
{
    QStringList words = text.split(' ', QString::SkipEmptyParts);
    Command command;

    {
        std::lock_guard<std::mutex> lock(this->mutex_);

        if (words.length() == 0) {
            return text;
        }

        QString commandName = words[0];

        // works in a valid twitch channel and /whispers, etc...
        if (!dryRun && channel->isTwitchChannel()) {
            if (commandName == "/w") {
                if (words.length() <= 2) {
                    return "";
                }

                auto app = getApp();

                MessageBuilder b;

                b.emplace<TimestampElement>();
                b.emplace<TextElement>(app->accounts->twitch.getCurrent()->getUserName(),
                                       MessageElement::Text, MessageColor::Text,
                                       FontStyle::ChatMediumBold);
                b.emplace<TextElement>("->", MessageElement::Text);
                b.emplace<TextElement>(words[1] + ":", MessageElement::Text, MessageColor::Text,
                                       FontStyle::ChatMediumBold);

                QString rest = "";

                for (int i = 2; i < words.length(); i++) {
                    rest += words[i] + " ";
                }

                b.emplace<TextElement>(rest, MessageElement::Text);
                b.getMessage()->flags |= Message::DoNotTriggerNotification;

                app->twitch.server->whispersChannel->addMessage(b.getMessage());

                app->twitch.server->getWriteConnection()->sendRaw("PRIVMSG #jtv :" + text + "\r\n");

                if (app->settings->inlineWhispers) {
                    app->twitch.server->forEachChannel(
                        [&b](ChannelPtr _channel) { _channel->addMessage(b.getMessage()); });
                }

                return "";
            }
        }

        // check if default command exists
        auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());

        // works only in a valid twitch channel
        if (!dryRun && twitchChannel != nullptr) {
            if (commandName == "/debug-args") {
                QString msg = QApplication::instance()->arguments().join(' ');

                channel->addMessage(Message::createSystemMessage(msg));

                return "";
            } else if (commandName == "/uptime") {
                const auto &streamStatus = twitchChannel->getStreamStatus();

                QString messageText =
                    streamStatus.live ? streamStatus.uptime : "Channel is not live.";

                channel->addMessage(Message::createSystemMessage(messageText));

                return "";
            } else if (commandName == "/ignore" && words.size() >= 2) {
                auto app = getApp();

                auto user = app->accounts->twitch.getCurrent();
                auto target = words.at(1);

                if (user->isAnon()) {
                    channel->addMessage(
                        Message::createSystemMessage("You must be logged in to ignore someone"));
                    return "";
                }

                user->ignore(target, [channel](auto resultCode, const QString &message) {
                    channel->addMessage(Message::createSystemMessage(message));
                });

                return "";
            } else if (commandName == "/unignore" && words.size() >= 2) {
                auto app = getApp();

                auto user = app->accounts->twitch.getCurrent();
                auto target = words.at(1);

                if (user->isAnon()) {
                    channel->addMessage(
                        Message::createSystemMessage("You must be logged in to ignore someone"));
                    return "";
                }

                user->unignore(target, [channel](auto resultCode, const QString &message) {
                    channel->addMessage(Message::createSystemMessage(message));
                });

                return "";
            } else if (commandName == "/logs") {
                if (words.size() < 2) {
                    channel->addMessage(
                        Message::createSystemMessage("Usage: /logs [user] (channel)"));
                    return "";
                }
                auto app = getApp();

                auto logs = new LogsPopup();
                QString target;

                if (words.at(1).at(0) == "@") {
                    target = words.at(1).mid(1);
                } else {
                    target = words.at(1);
                }

                if (words.size() == 3) {
                    QString channelName = words.at(2);
                    if (words.at(2).at(0) == "#") {
                        channelName = words.at(2).mid(1);
                    }
                    auto logsChannel = app->twitch.server->getChannelOrEmpty(channelName);
                    if (logsChannel == nullptr) {
                    } else {
                        logs->setInfo(logsChannel, target);
                    }
                } else {
                    logs->setInfo(channel, target);
                }
                logs->setAttribute(Qt::WA_DeleteOnClose);
                logs->show();
                return "";
            }
        }

        // check if custom command exists
        auto it = this->commandsMap_.find(commandName);
        if (it == this->commandsMap_.end()) {
            return text;
        }

        command = it.value();
    }

    return this->execCustomCommand(words, command);
}

QString CommandController::execCustomCommand(const QStringList &words, const Command &command)
{
    QString result;

    static QRegularExpression parseCommand("(^|[^{])({{)*{(\\d+\\+?)}");

    int lastCaptureEnd = 0;

    auto globalMatch = parseCommand.globalMatch(command.func);
    int matchOffset = 0;

    while (true) {
        QRegularExpressionMatch match = parseCommand.match(command.func, matchOffset);

        if (!match.hasMatch()) {
            break;
        }

        result += command.func.mid(lastCaptureEnd, match.capturedStart() - lastCaptureEnd + 1);

        lastCaptureEnd = match.capturedEnd();
        matchOffset = lastCaptureEnd - 1;

        QString wordIndexMatch = match.captured(3);

        bool plus = wordIndexMatch.at(wordIndexMatch.size() - 1) == '+';
        wordIndexMatch = wordIndexMatch.replace("+", "");

        bool ok;
        int wordIndex = wordIndexMatch.replace("=", "").toInt(&ok);
        if (!ok || wordIndex == 0) {
            result += "{" + match.captured(3) + "}";
            continue;
        }

        if (words.length() <= wordIndex) {
            continue;
        }

        if (plus) {
            bool first = true;
            for (int i = wordIndex; i < words.length(); i++) {
                if (!first) {
                    result += " ";
                }
                result += words[i];
                first = false;
            }
        } else {
            result += words[wordIndex];
        }
    }

    result += command.func.mid(lastCaptureEnd);

    if (result.size() > 0 && result.at(0) == '{') {
        result = result.mid(1);
    }

    return result.replace("{{", "{");
}

QStringList CommandController::getDefaultTwitchCommandList()
{
    QStringList l = TWITCH_DEFAULT_COMMANDS;
    l += "/uptime";

    return l;
}

}  // namespace chatterino
<|endoftext|>


<commit_before>/*
    ccserve: credit-card generator server
    Copyright (C) 2015 Graham King

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <memory>
#include <unordered_map>
#include <ctime>
#include <string>
#include <array>

#include <asio.hpp>

#include "ccgen.h"

#define PORT 1212

const std::string headers {R"MARKER(HTTP/1.0 200 OK
Connection: close
Access-Control-Allow-Origin: *
Content-type: text/html
Content-length: )MARKER"};

void handle_in(unsigned int, const asio::error_code&);
void handle_out(unsigned int conn_id, const asio::error_code& e, std::size_t size);
void done(const unsigned int, const asio::error_code&, std::size_t);

const char* time_format {"%Y-%m-%dT%H:%M:%S"};
void now(std::array<char, 20>& out) {
	std::time_t t = std::time(nullptr);
	std::tm tm = *std::localtime(&t);
	std::strftime(out.begin(), 20, time_format, &tm);
}

class Conn {
public:
	Conn(unsigned int conn_id, asio::io_service& io) : sock(io), id(conn_id) { };
	void read();
	void write();
	void close() { sock.close(); };
	asio::ip::tcp::socket& get_socket() { return sock; };
private:
	asio::ip::tcp::socket sock;
	unsigned int id;
	asio::streambuf b;
};

void Conn::write()
{
	std::string out {headers};
	std::string ccbody {ccgen()};
	out += std::to_string(ccbody.size());
	out += +"\r\n\r\n";
	out += ccbody;

	asio::async_write(
		sock,
		asio::buffer(out),
		std::bind(&done, id, std::placeholders::_1, std::placeholders::_2)
	);
}

void Conn::read()
{
	asio::async_read_until(
		sock,
		b,
		"\r\n\r\n",
		std::bind(&handle_out, id, std::placeholders::_1, std::placeholders::_2)
	);
}

class Tcp_server
{
public:
	Tcp_server(asio::io_service& io)
		: acceptor(io), conns(32), next_conn_id(0)
	{
		std::array<char, 20> nowout;
		now(nowout);
		std::cout << '[' << nowout.begin() << "] ccserve start\n";

		asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), PORT);
		acceptor.open(endpoint.protocol());
		acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true));
		acceptor.bind(endpoint);
		acceptor.listen();

		accept_next();
	}

	void accept_next() {
		unsigned int cid = next_conn_id;
		next_conn_id = ++next_conn_id % std::numeric_limits<int>::max();
		auto res = conns.emplace(
			cid,
			new Conn(cid, acceptor.get_io_service())
		);
		auto iter = res.first;
		acceptor.async_accept(
			iter->second->get_socket(),
			std::bind(&handle_in, cid, std::placeholders::_1)
		);
	}

	void read_conn(const unsigned int conn_id) { conns[conn_id]->read(); }

	void write_conn(const unsigned int conn_id) { conns[conn_id]->write(); }

	void close(const unsigned int conn_id) {
		Conn* c = conns[conn_id];
		c->close();
		delete c;
		conns.erase(conn_id);
	}

private:
	asio::ip::tcp::acceptor acceptor;
	std::unordered_map<unsigned int, Conn*> conns;
	unsigned int next_conn_id;
};

std::unique_ptr<Tcp_server> server;

// completion handler
void done(
		const unsigned int conn_id,
		const asio::error_code& error,
		std::size_t bytes_transferred)
{
	server->close(conn_id);
}

// accept handler
void handle_in(unsigned int conn_id, const asio::error_code& error)
{
	server->read_conn(conn_id);
	server->accept_next();
}

// called when we have consumed input
void handle_out(unsigned int conn_id, const asio::error_code& e, std::size_t size)
{
	server->write_conn(conn_id);
}

int main()
{
	try
	{
		asio::io_service io;
		server = std::make_unique<Tcp_server>(io);
		io.run();
	}
	catch (std::exception& e)
	{
		std::cerr << "Global exception: " << e.what() << std::endl;
		return 1;
	}
	return 0;
}
<commit_msg>This RAII thing is really cool.<commit_after>/*
    ccserve: credit-card generator server
    Copyright (C) 2015 Graham King

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <memory>
#include <unordered_map>
#include <ctime>
#include <string>
#include <array>

#include <asio.hpp>

#include "ccgen.h"

#define PORT 1212

const std::string headers {R"MARKER(HTTP/1.0 200 OK
Connection: close
Access-Control-Allow-Origin: *
Content-type: text/html
Content-length: )MARKER"};

void handle_in(unsigned int, const asio::error_code&);
void handle_out(unsigned int conn_id, const asio::error_code& e, std::size_t size);
void done(const unsigned int, const asio::error_code&, std::size_t);

const char* time_format {"%Y-%m-%dT%H:%M:%S"};
void now(std::array<char, 20>& out) {
	std::time_t t = std::time(nullptr);
	std::tm tm = *std::localtime(&t);
	std::strftime(out.begin(), 20, time_format, &tm);
}

class Conn {
public:
	Conn(unsigned int conn_id, asio::io_service& io) : sock(io), id(conn_id) { };
	~Conn() { sock.close(); }
	void read();
	void write();
	asio::ip::tcp::socket& get_socket() { return sock; };
private:
	asio::ip::tcp::socket sock;
	unsigned int id;
	asio::streambuf b;
};

void Conn::write()
{
	std::string out {headers};
	std::string ccbody {ccgen()};
	out += std::to_string(ccbody.size());
	out += +"\r\n\r\n";
	out += ccbody;

	asio::async_write(
		sock,
		asio::buffer(out),
		std::bind(&done, id, std::placeholders::_1, std::placeholders::_2)
	);
}

void Conn::read()
{
	asio::async_read_until(
		sock,
		b,
		"\r\n\r\n",
		std::bind(&handle_out, id, std::placeholders::_1, std::placeholders::_2)
	);
}

class Tcp_server
{
public:
	Tcp_server(asio::io_service& io)
		: acceptor(io), conns(32), next_conn_id(0)
	{
		std::array<char, 20> nowout;
		now(nowout);
		std::cout << '[' << nowout.begin() << "] ccserve start\n";

		asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), PORT);
		acceptor.open(endpoint.protocol());
		acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true));
		acceptor.bind(endpoint);
		acceptor.listen();

		accept_next();
	}

	~Tcp_server() // is this ever called?
	{
		asio::error_code ec;
		acceptor.close(ec);
		if (ec) { std::cerr << "Error closing acceptor: " << ec << std::endl; }
	}

	void accept_next() {
		unsigned int cid = next_conn_id;
		next_conn_id = ++next_conn_id % std::numeric_limits<int>::max();
		auto res = conns.emplace(
			cid,
			std::make_unique<Conn>(cid, acceptor.get_io_service())
		);
		auto iter = res.first;
		acceptor.async_accept(
			iter->second->get_socket(),
			std::bind(&handle_in, cid, std::placeholders::_1)
		);
	}

	void read_conn(const unsigned int conn_id) { conns[conn_id]->read(); }

	void write_conn(const unsigned int conn_id) { conns[conn_id]->write(); }

	void close(const unsigned int conn_id) {
		// this should make the unique_ptr go out of scope,
		// calling Conn's destructor, which closes the socket.
		conns.erase(conn_id);
	}

private:
	asio::ip::tcp::acceptor acceptor;
	std::unordered_map<unsigned int, std::unique_ptr<Conn>> conns;
	unsigned int next_conn_id;
};

std::unique_ptr<Tcp_server> server;

// completion handler
void done(
		const unsigned int conn_id,
		const asio::error_code& error,
		std::size_t bytes_transferred)
{
	server->close(conn_id);
}

// accept handler
void handle_in(unsigned int conn_id, const asio::error_code& error)
{
	server->read_conn(conn_id);
	server->accept_next();
}

// called when we have consumed input
void handle_out(unsigned int conn_id, const asio::error_code& e, std::size_t size)
{
	server->write_conn(conn_id);
}

int main()
{
	try
	{
		asio::io_service io;
		server = std::make_unique<Tcp_server>(io);
		io.run();
	}
	catch (std::exception& e)
	{
		std::cerr << "Global exception: " << e.what() << std::endl;
		return 1;
	}
	return 0;
}
<|endoftext|>


<commit_before>/*
The OpenTRV project licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the Licence is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the Licence for the
specific language governing permissions and limitations
under the Licence.

Author(s) / Copyright (s): Damon Hart-Davis 2016
*/

/*
 * OTRadValve ModelledRadValve thermal model tests.
 *
 * Aim is to, for example, model different
 * radiator efficacies, valve behaviours, boiler speeds, radio loss, etc;
 * to ensure that responsiveness, temperature regulation
 * and valve movement/noise/energy are OK.
 *
 * Model for all-in-one and split unit configurations.
 *
 * This can even be extended to DHW tests.
 */

#include <gtest/gtest.h>
#include <cstdint>
#include <cstdio>

#include "OTRadValve_AbstractRadValve.h"
#include "OTRadValve_ModelledRadValve.h"

// Holds references to a valve and temperature sensor
// and models the how the former drives the latter
// given the characteristics of the room, boiler, etc.
// NOTE: All constants are the absolute values for the room.
// Heat capacities etc. should be calculated from room size etc. before feeding into the model!
namespace TMB {

bool verbose = false;
bool splitUnit = false;

class ThermalModelBase
    {
    protected:
        // Simulated valve, internal.
        OTRadValve::RadValveMock radValveInternal;

        // Simulated room temperature, internal.
        OTV0P2BASE::TemperatureC16Mock roomTemperatureInternal;

        // Constants & variables
        const float conductance_21;
        const float conductance_10;
        const float conductance_0W;
        const float capacitance_2;
        const float capacitance_1;
        const float capacitance_0;
        float airTemperature;
        float roomTemp;
        float t1;
        float t0;
        float outsideTemp;
        const float radiatorConductance;
        const float maxRadiatorTemp;
        float valveTemp;
        // Internal methods
        /**
         * @brief   Calculate heat transfer through a thermal resistance. Flow from temp1 to temp2 is positive.
         */
        static float heatTransfer(const float conductance, const float temp1, const float temp2)
        {
            return conductance * (temp1 - temp2);
        }
        /**
         * @brief   Calculate heat input this interval by radiator.
         * @note    Heat flow into the room is positive.
         * @note    Assumes that radiator temperature (and therefore heat input):
         *          - increases linearly.
         *          - increases monotonically.
         *          - Cannot be below air temperature (the radiator cannot sink heat).
         * @retval  Heat transfer into room from radiator, in J
         */
        float calcHeatFlowRad(const float airTemp, const uint8_t radValveOpenPC) {
            // convert radValveOpenPC to radiator temp (badly)
            const float radTemp = (2.0 * (float)radValveOpenPC) - 80;
            // Making sure the radiator temp does not exceed sensible values
            const float scaledRadTemp = (radTemp < maxRadiatorTemp) ? radTemp : maxRadiatorTemp;
            // Calculate heat transfer, making sure rad temp cannot go below air temperature.
            return (radTemp > airTemp) ? (heatTransfer(radiatorConductance, scaledRadTemp, airTemp)) : 0.0;
        }
        /**
         * @brief   Calculate temp seen by valve this interval.
         * @note    Heat flow into the room is positive.
         * @note    Assumes that radiator temperature (and therefore heat input):
         *          - increases linearly.
         *          - increases monotonically.
         *          - Cannot be below air temperature (the radiator cannot sink heat).
         * @retval  Heat transfer into room from radiator, in J
         */
        float calcValveTemp(const float airTemp, const float localTemp, const float heatFlowFromRad) {
            static constexpr float thermalConductanceRad = 0.05f;  // fixme literal is starting estimate for thermal resistance
            static constexpr float thermalConductanceRoom = 10.0f;
            const float heatIn = heatFlowFromRad * thermalConductanceRad;
            const float heatOut = heatTransfer(thermalConductanceRoom, localTemp, airTemp);
            const float valveHeatFlow = heatIn - heatOut;
            const float newLocalTemp = localTemp + (valveHeatFlow / 5000);  // fixme literal is starting estimate for thermal capacitance
            return newLocalTemp;
        }

    public:
        ThermalModelBase(const float _conductance_21,      // [W/K]
                         const float _conductance_10,   // [J/K]
                         const float _conductance_0W,   // [W/K]
                         const float _capacitance_2,   // [J/K]
                         const float _capacitance_1,   // [J/K]
                         const float _capacitance_0,   // [J/K]
                         const float startTemp,             // [C]
                         const float _outsideTemp = 0.0,          // [C]
                         const float _radiatorConductance = 25.0,  // [W/K]
                         const float _maxRadiatorTemp = 70.0)      // [C]
                       : conductance_21(_conductance_21),
                         conductance_10(_conductance_10),
                         conductance_0W(_conductance_0W),
                         capacitance_2(_capacitance_2),
                         capacitance_1(_capacitance_1),
                         capacitance_0(_capacitance_0),
                         airTemperature(startTemp),
                         roomTemp(startTemp),
                         t1(startTemp),
                         t0(startTemp),
                         outsideTemp(_outsideTemp),
                         radiatorConductance(_radiatorConductance),
                         maxRadiatorTemp(_maxRadiatorTemp),
                         valveTemp(startTemp)
//                         storedHeat(0.0)
        {
            const float temperatureC16 = (int16_t)(startTemp * 16.0);
//            storedHeat = startTemp * storageCapacitance;
            roomTemperatureInternal.set(temperatureC16);
            radValveInternal.set(0);
        };

        // Read-only view of simulated room temperature.
        const OTV0P2BASE::TemperatureC16Base &roomTemperature = roomTemperatureInternal;

        // Read-only view of simulated radiator valve.
        const OTRadValve::AbstractRadValve &radValve = radValveInternal;

        // Calculate new temperature
        void calcNewAirTemperature(uint8_t radValveOpenPC) {
            radValveInternal.set(radValveOpenPC);
            // Calc heat in from rad
            const float heat_in = calcHeatFlowRad(airTemperature, radValveInternal.get());
            // Calc heat flow from seg2 to seg1
            const float heat_21 = heat_in - heatTransfer(conductance_21, roomTemp, t1);
            // Calc heat flow from seg1 to seg0
            const float heat_10 = heatTransfer(conductance_21, roomTemp, t1) - heatTransfer(conductance_10, t1, t0);
            // Calc heat flow from seg0 to world.
            const float heat_out = heatTransfer(conductance_10, t1, t0) - heatTransfer(conductance_0W, t0, outsideTemp);
            // Calc new temps.
            roomTemp += heat_21 / capacitance_2;
            t1 += heat_10 / capacitance_1;
            t0 += heat_out / capacitance_0;
            // Calc temp of thermostat. This is the same as the room temp in a splot unit.
            if(!splitUnit) valveTemp = calcValveTemp(roomTemp, valveTemp, heat_in);
            else valveTemp = roomTemp;
            if(verbose) { }  // todo put print out in here
        }
        float getAirTemperature() { return roomTemp; }
        float getValveTemperature() {return valveTemp;}
    };
}

//// Basic tests of RadValveModel room with valve fully off.
//// Hijacked as a test against my python model (DE20170207)
//TEST(ModelledRadValveThermalModel, roomModelCold)
//{
//    TMB::ThermalModelBase model(38.4, 1000000.0, 1.0, 41780.3625, 20.0);
//    for(auto i = 0; i < 1000; ++i) {
//        model.calcNewAirTemperature(0);
//    }
//    EXPECT_NEAR(8.1, model.getAirTemperature(), 0.01); // Value for same inputs in my python model is: 8.096024519493666 C
//}
//
//// Basic tests of RadValveModel room with valve fully on.
//// Hijacked as a test against my python model (DE20170207)
//TEST(ModelledRadValveThermalModel,roomModelHot)
//{
//    TMB::ThermalModelBase model(38.4, 1000000.0, 1.0, 41780.3625, 20.0);
//    for(auto i = 0; i < 1000; ++i) {
//        model.calcNewAirTemperature(100);
//    }
//    EXPECT_EQ(100, model.radValve.get());
//    EXPECT_NEAR(25.88, model.getAirTemperature(), 0.01);
//}

// Basic tests of RadValveModel room with valve fully on.
// Starts at an unrealistically high temperature (40C).
//TEST(ModelledRadValveThermalModel, roomHotBasic)
//{
//    // Room start temp
//    const float startTempC = 16.0f;
//    // Target temperature without setback.
//    const float targetTempC = 19.0f;
//    // Valve starts fully shut.
//    uint8_t valvePCOpen = 100;
//    //
//    OTRadValve::ModelledRadValveInputState is0((uint_fast16_t)(startTempC * 16));
//    is0.targetTempC = targetTempC;
//    OTRadValve::ModelledRadValveState rs0;
//
//    TMB::ThermalModelBase model(500, 300, 50,
//                                350000, 1300000, 7000000,
//                                startTempC);
//
//    // Keep track of maximum and minimum room temps.
//    float maxRoomTempC = 0.0;
//    float minRoomTempC = 100.0;
//    for(auto i = 0; i < 20000; ++i) {
//        const float curTempC = model.getAirTemperature(); // current air temperature in C
//        if(0 == (i % 60)) {
//            if((60 * 60 * 2) == i) valvePCOpen = 0;  // run for one hour then shut.
////            fprintf(stderr, "[ \"%u\", \"\", {\"T|C16\": %.2f, \"tT|C\": %.2f, \"v|%%\": %u} ]\n", i, curTempC, targetTempC, valvePCOpen);
//        }
//        model.calcNewAirTemperature(valvePCOpen);
//        maxRoomTempC = (maxRoomTempC > curTempC) ? maxRoomTempC : curTempC;
//        minRoomTempC = ((minRoomTempC < curTempC) && (1000 < i)) ? minRoomTempC : curTempC;  // avoid comparing during initial warm-up.
//    }
//}

/**
 * Helper class to handle updating and storing state of TRV..
 */
namespace TMTRHC {
static constexpr uint_fast8_t valveUpdateTime = 60;  // Length of valve update cycle.

class ThermalModelValve
{
protected:
    uint_fast8_t valvePCOpen;
    OTRadValve::ModelledRadValveInputState is0;
    OTRadValve::ModelledRadValveState rs0;
public:
    ThermalModelValve(const uint_fast8_t startValvePCOpen, const float targetTemp) : valvePCOpen(startValvePCOpen)
        { is0.targetTempC = targetTemp; }
    void tick(const float curTempC) {
        is0.setReferenceTemperatures((uint_fast16_t)(curTempC * 16));
        rs0.tick(valvePCOpen, is0, NULL);
    }
    float getValvePCOpen() { return valvePCOpen; }
    float getTargetTempC() { return is0.targetTempC; }
};
}
TEST(ModelledRadValveThermalModel, roomHotControlled)
{
    bool verbose = false;
    TMB::splitUnit = false;
    // Room start temp
    const float startTempC = 16.0f;
    const float targetTempC = 19.0f;
    // Keep track of maximum and minimum room temps.
//    float maxRoomTempC = 0.0;
//    float minRoomTempC = 100.0;
    // keep track of valve positions.
    const uint_fast8_t startingValvePCOpen = 0;
    // Set up.
    TMTRHC::ThermalModelValve valve(startingValvePCOpen, targetTempC);
    TMB::ThermalModelBase model(500, 300, 50,
                                350000, 1300000, 7000000,
                                startTempC);
    // Delay in radiator responding to change in valvePCOpen. Should possibly be asymmetric. todo move into room model.
    std::vector<uint_fast8_t> radDelay(5, startingValvePCOpen);
    for(auto i = 0; i < 20000; ++i) {
        const float curTempC = model.getValveTemperature(); // current air temperature in C
        if(0 == (i % TMTRHC::valveUpdateTime)) {  // once per minute tasks.
            const uint_fast8_t valvePCOpen = valve.getValvePCOpen();
            if (verbose) {
                fprintf(stderr, "[ \"%u\", \"\", {\"T|C\": %.2f, \"TV|C\": %.2f, \"tT|C\": %.2f, \"v|%%\": %u} ]\n", i, model.getAirTemperature(), curTempC, targetTempC, valvePCOpen);
            }
            valve.tick(curTempC);
            radDelay.erase(radDelay.begin());
            radDelay.push_back(valvePCOpen);
        }
        model.calcNewAirTemperature(radDelay.front());
//        maxRoomTempC = (maxRoomTempC > curTempC) ? maxRoomTempC : curTempC;
//        minRoomTempC = ((minRoomTempC < curTempC) && (1000 < i)) ? minRoomTempC : curTempC;  // avoid comparing during initial warm-up.
    }
}
/* TODO

Test for sticky / jammed / closed value calling for heat in stable temp room running boiler continually: TODO-1096

Test for sensible outcomes in difficult cases such as:
  * DHW (esp needing glacial as per Bo).
  * All-in-one (TRV1.x) on flow end of rad very slow to heat up
    so valve must let whole rad get warm to let room get warm
    (eg see 1g 2017/01/14 12:00 to 15:00 om tag 20170114-responsiveness).
  * All-in-one (TRV1.x) on rad with very poor air circulation.
  * Split unit (REV2+FHT8V style) with sensor close to and far from radiator.
  * All-in-one or split unit in draughty room.
  * All-in-one or split unit in room with door or window opened on cold day.
  * Behaviour in well-insulated (or otherwise) house
    with central timer only set for a few hours per day.
 */
<commit_msg>Added some docs<commit_after>/*
The OpenTRV project licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the Licence is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the Licence for the
specific language governing permissions and limitations
under the Licence.

Author(s) / Copyright (s): Damon Hart-Davis 2016
*/

/*
 * OTRadValve ModelledRadValve thermal model tests.
 *
 * Aim is to, for example, model different
 * radiator efficacies, valve behaviours, boiler speeds, radio loss, etc;
 * to ensure that responsiveness, temperature regulation
 * and valve movement/noise/energy are OK.
 *
 * Model for all-in-one and split unit configurations.
 *
 * This can even be extended to DHW tests.
 */

#include <gtest/gtest.h>
#include <cstdint>
#include <cstdio>

#include "OTRadValve_AbstractRadValve.h"
#include "OTRadValve_ModelledRadValve.h"

// Holds references to a valve and temperature sensor
// and models the how the former drives the latter
// given the characteristics of the room, boiler, etc.
// NOTE: All constants are the absolute values for the room.
// Heat capacities etc. should be calculated from room size etc. before feeding into the model!
namespace TMB {

bool verbose = false;
bool splitUnit = false;

class ThermalModelBase
    {
    protected:
        // Simulated valve, internal.
        OTRadValve::RadValveMock radValveInternal;

        // Simulated room temperature, internal.
        OTV0P2BASE::TemperatureC16Mock roomTemperatureInternal;

        // Constants & variables
        const float conductance_21;
        const float conductance_10;
        const float conductance_0W;
        const float capacitance_2;
        const float capacitance_1;
        const float capacitance_0;
        float airTemperature;
        float roomTemp;
        float t1;
        float t0;
        float outsideTemp;
        const float radiatorConductance;
        const float maxRadiatorTemp;
        float valveTemp;
        // Internal methods
        /**
         * @brief   Calculate heat transfer through a thermal resistance. Flow from temp1 to temp2 is positive.
         */
        static float heatTransfer(const float conductance, const float temp1, const float temp2)
        {
            return conductance * (temp1 - temp2);
        }
        /**
         * @brief   Calculate heat input this interval by radiator.
         * @note    Heat flow into the room is positive.
         * @note    Assumes that radiator temperature (and therefore heat input):
         *          - increases linearly.
         *          - increases monotonically.
         *          - Cannot be below air temperature (the radiator cannot sink heat).
         * @retval  Heat transfer into room from radiator, in J
         */
        float calcHeatFlowRad(const float airTemp, const uint8_t radValveOpenPC) {
            // convert radValveOpenPC to radiator temp (badly)
            const float radTemp = (2.0 * (float)radValveOpenPC) - 80;
            // Making sure the radiator temp does not exceed sensible values
            const float scaledRadTemp = (radTemp < maxRadiatorTemp) ? radTemp : maxRadiatorTemp;
            // Calculate heat transfer, making sure rad temp cannot go below air temperature.
            return (radTemp > airTemp) ? (heatTransfer(radiatorConductance, scaledRadTemp, airTemp)) : 0.0;
        }
        /**
         * @brief   Calculate temp seen by valve this interval.
         * @note    Heat flow into the room is positive.
         * @note    Assumes that radiator temperature (and therefore heat input):
         *          - increases linearly.
         *          - increases monotonically.
         *          - Cannot be below air temperature (the radiator cannot sink heat).
         * @retval  Heat transfer into room from radiator, in J
         */
        float calcValveTemp(const float airTemp, const float localTemp, const float heatFlowFromRad) {
            static constexpr float thermalConductanceRad = 0.05f;  // fixme literal is starting estimate for thermal resistance
            static constexpr float thermalConductanceRoom = 10.0f;
            const float heatIn = heatFlowFromRad * thermalConductanceRad;
            const float heatOut = heatTransfer(thermalConductanceRoom, localTemp, airTemp);
            const float valveHeatFlow = heatIn - heatOut;
            const float newLocalTemp = localTemp + (valveHeatFlow / 5000);  // fixme literal is starting estimate for thermal capacitance
            return newLocalTemp;
        }

    public:
        ThermalModelBase(const float _conductance_21,      // [W/K]
                         const float _conductance_10,   // [J/K]
                         const float _conductance_0W,   // [W/K]
                         const float _capacitance_2,   // [J/K]
                         const float _capacitance_1,   // [J/K]
                         const float _capacitance_0,   // [J/K]
                         const float startTemp,             // [C]
                         const float _outsideTemp = 0.0,          // [C]
                         const float _radiatorConductance = 25.0,  // [W/K]
                         const float _maxRadiatorTemp = 70.0)      // [C]
                       : conductance_21(_conductance_21),
                         conductance_10(_conductance_10),
                         conductance_0W(_conductance_0W),
                         capacitance_2(_capacitance_2),
                         capacitance_1(_capacitance_1),
                         capacitance_0(_capacitance_0),
                         airTemperature(startTemp),
                         roomTemp(startTemp),
                         t1(startTemp),
                         t0(startTemp),
                         outsideTemp(_outsideTemp),
                         radiatorConductance(_radiatorConductance),
                         maxRadiatorTemp(_maxRadiatorTemp),
                         valveTemp(startTemp)
//                         storedHeat(0.0)
        {
            const float temperatureC16 = (int16_t)(startTemp * 16.0);
//            storedHeat = startTemp * storageCapacitance;
            roomTemperatureInternal.set(temperatureC16);
            radValveInternal.set(0);
        };

        // Read-only view of simulated room temperature.
        const OTV0P2BASE::TemperatureC16Base &roomTemperature = roomTemperatureInternal;

        // Read-only view of simulated radiator valve.
        const OTRadValve::AbstractRadValve &radValve = radValveInternal;

        // Calculate new temperature
        void calcNewAirTemperature(uint8_t radValveOpenPC) {
            radValveInternal.set(radValveOpenPC);
            // Calc heat in from rad
            const float heat_in = calcHeatFlowRad(airTemperature, radValveInternal.get());
            // Calc heat flow from seg2 to seg1
            const float heat_21 = heat_in - heatTransfer(conductance_21, roomTemp, t1);
            // Calc heat flow from seg1 to seg0
            const float heat_10 = heatTransfer(conductance_21, roomTemp, t1) - heatTransfer(conductance_10, t1, t0);
            // Calc heat flow from seg0 to world.
            const float heat_out = heatTransfer(conductance_10, t1, t0) - heatTransfer(conductance_0W, t0, outsideTemp);
            // Calc new temps.
            roomTemp += heat_21 / capacitance_2;
            t1 += heat_10 / capacitance_1;
            t0 += heat_out / capacitance_0;
            // Calc temp of thermostat. This is the same as the room temp in a splot unit.
            if(!splitUnit) valveTemp = calcValveTemp(roomTemp, valveTemp, heat_in);
            else valveTemp = roomTemp;
            if(verbose) { }  // todo put print out in here
        }
        float getAirTemperature() { return roomTemp; }
        float getValveTemperature() {return valveTemp;}
    };

/**
 * @brief   Helper function that prints a JSON frame in the style of an OpenTRV frame.
 * @param   i: current model iteration
 * @param   airTempC: average air temperature of the room (key 'T|C').
 * @param   valveTempC: temperature as measured by the TRV (key 'TV|C). This should be the same as airTempC in a split unit TRV.
 * @param   targetTempC: target room temperature (key 'tT|C').
 * @param   valvePCOpen: current valve position in % (key 'v|%').
 */
static void printFrame(const unsigned int i, const float airTempC, const float valveTempC, const float targetTempC, const uint_fast8_t valvePCOpen) {
    fprintf(stderr, "[ \"%u\", \"\", {\"T|C\": %.2f, \"TV|C\": %.2f, \"tT|C\": %.2f, \"v|%%\": %u} ]\n",
            i, airTempC, valveTempC, targetTempC, valvePCOpen);
}
}

/**
 * @brief   Test an all in one unit.
 */
namespace TMTRHC {
static constexpr uint_fast8_t valveUpdateTime = 60;  // Length of valve update cycle in seconds.
/**
 * Helper class to handle updating and storing state of TRV.
 */
class ThermalModelValve
{
protected:
    uint_fast8_t valvePCOpen;
    OTRadValve::ModelledRadValveInputState is0;
    OTRadValve::ModelledRadValveState rs0;
public:
    ThermalModelValve(const uint_fast8_t startValvePCOpen, const float targetTemp) : valvePCOpen(startValvePCOpen)
        { is0.targetTempC = targetTemp; }
    /**
     * @brief   Set current temperature at valve and calculate new valve state.
     *          Should be called once per valve update cycle (see valveUpdateTime).
     */
    void tick(const float curTempC) {
        is0.setReferenceTemperatures((uint_fast16_t)(curTempC * 16));
        rs0.tick(valvePCOpen, is0, NULL);
    }
    float getValvePCOpen() { return valvePCOpen; }
    float getTargetTempC() { return is0.targetTempC; }
};
}
TEST(ModelledRadValveThermalModel, roomCold)
{
    bool verbose = false;
    TMB::splitUnit = false;
    // Room start temp
    const float startTempC = 16.0f;
    const float targetTempC = 19.0f;
    // Keep track of maximum and minimum room temps.
//    float maxRoomTempC = 0.0;
//    float minRoomTempC = 100.0;
    // keep track of valve positions.
    const uint_fast8_t startingValvePCOpen = 0;
    // Set up.
    TMTRHC::ThermalModelValve valve(startingValvePCOpen, targetTempC);
    TMB::ThermalModelBase model(500, 300, 50,
                                350000, 1300000, 7000000,
                                startTempC);
    // Delay in radiator responding to change in valvePCOpen. Should possibly be asymmetric. todo move into room model.
    std::vector<uint_fast8_t> radDelay(5, startingValvePCOpen);
    for(auto i = 0; i < 20000; ++i) {
        const float valveTempC = model.getValveTemperature(); // current air temperature in C
        if(0 == (i % TMTRHC::valveUpdateTime)) {  // once per minute tasks.
            const uint_fast8_t valvePCOpen = valve.getValvePCOpen();
            if (verbose) {
                TMB::printFrame(i, model.getAirTemperature(), valveTempC, targetTempC, valvePCOpen);
            }
            valve.tick(valveTempC);
            radDelay.erase(radDelay.begin());
            radDelay.push_back(valvePCOpen);
        }
        model.calcNewAirTemperature(radDelay.front());
//        maxRoomTempC = (maxRoomTempC > curTempC) ? maxRoomTempC : curTempC;
//        minRoomTempC = ((minRoomTempC < curTempC) && (1000 < i)) ? minRoomTempC : curTempC;  // avoid comparing during initial warm-up.
    }
}

TEST(ModelledRadValveThermalModel, roomHot)
{
    bool verbose = true;
    TMB::splitUnit = false;
    // Room start temp
    const float startTempC = 25.0f;
    const float targetTempC = 19.0f;
    // keep track of valve positions.
    const uint_fast8_t startingValvePCOpen = 0;
    // Set up.
    TMTRHC::ThermalModelValve valve(startingValvePCOpen, targetTempC);
    TMB::ThermalModelBase model(500, 300, 50,
                                350000, 1300000, 7000000,
                                startTempC);
    // Delay in radiator responding to change in valvePCOpen. Should possibly be asymmetric. todo move into room model.
    std::vector<uint_fast8_t> radDelay(5, startingValvePCOpen);
    for(auto i = 0; i < 20000; ++i) {
        const float valveTempC = model.getValveTemperature(); // current air temperature in C
        if(0 == (i % TMTRHC::valveUpdateTime)) {  // once per minute tasks.
            const uint_fast8_t valvePCOpen = valve.getValvePCOpen();
            if (verbose) {
                TMB::printFrame(i, model.getAirTemperature(), valveTempC, targetTempC, valvePCOpen);
            }
            valve.tick(valveTempC);
            radDelay.erase(radDelay.begin());
            radDelay.push_back(valvePCOpen);
        }
        model.calcNewAirTemperature(radDelay.front());
    }
}
/* TODO

Test for sticky / jammed / closed value calling for heat in stable temp room running boiler continually: TODO-1096

Test for sensible outcomes in difficult cases such as:
  * DHW (esp needing glacial as per Bo).
  * All-in-one (TRV1.x) on flow end of rad very slow to heat up
    so valve must let whole rad get warm to let room get warm
    (eg see 1g 2017/01/14 12:00 to 15:00 om tag 20170114-responsiveness).
  * All-in-one (TRV1.x) on rad with very poor air circulation.
  * Split unit (REV2+FHT8V style) with sensor close to and far from radiator.
  * All-in-one or split unit in draughty room.
  * All-in-one or split unit in room with door or window opened on cold day.
  * Behaviour in well-insulated (or otherwise) house
    with central timer only set for a few hours per day.
 */
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "qt5nodeinstanceserver.h"

#include <QQuickItem>
#include <QQuickView>

#include <designersupport.h>
#include <addimportcontainer.h>
#include <createscenecommand.h>

namespace QmlDesigner {

Qt5NodeInstanceServer::Qt5NodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient)
    : NodeInstanceServer(nodeInstanceClient),
      m_designerSupport(new DesignerSupport)
{
    addImportString("import QtQuick 2.0\n");
    DesignerSupport::activateDesignerAnimationMode();
    DesignerSupport::deactivateComponentComplete();
}

Qt5NodeInstanceServer::~Qt5NodeInstanceServer()
{
    delete quickView();
    delete m_designerSupport;
    m_designerSupport = 0;
}

QQuickView *Qt5NodeInstanceServer::quickView() const
{
    return m_quickView.data();
}

void Qt5NodeInstanceServer::initializeView(const QVector<AddImportContainer> &/*importVector*/)
{
    Q_ASSERT(!quickView());

    m_quickView = new QQuickView;
    DesignerSupport::createOpenGLContext(m_quickView.data());
}

QQmlView *Qt5NodeInstanceServer::declarativeView() const
{
    return 0;
}

QQmlEngine *Qt5NodeInstanceServer::engine() const
{
    if (quickView())
        return quickView()->engine();

    return 0;
}

void Qt5NodeInstanceServer::resizeCanvasSizeToRootItemSize()
{
}

void Qt5NodeInstanceServer::resetAllItems()
{
    foreach (QQuickItem *item, allItems())
        DesignerSupport::resetDirty(item);
}

QList<ServerNodeInstance> Qt5NodeInstanceServer::setupScene(const CreateSceneCommand &command)
{
    setupFileUrl(command.fileUrl());
    setupImports(command.imports());
    setupDummyData(command.fileUrl());

    QList<ServerNodeInstance> instanceList = setupInstances(command);

    quickView()->resize(rootNodeInstance().boundingRect().size().toSize());

    return instanceList;
}

QList<QQuickItem*> subItems(QQuickItem *parentItem)
{
    QList<QQuickItem*> itemList;
    itemList.append(parentItem->childItems());

    foreach (QQuickItem *childItem, parentItem->childItems())
        itemList.append(subItems(childItem));

    return itemList;
}

QList<QQuickItem*> Qt5NodeInstanceServer::allItems() const
{
    QList<QQuickItem*> itemList;

    if (quickView()) {
        itemList.append(quickView()->rootObject());
        itemList.append(subItems(quickView()->rootObject()));
    }

    return itemList;
}

void Qt5NodeInstanceServer::refreshBindings()
{
    DesignerSupport::refreshExpressions(context());
}

DesignerSupport *Qt5NodeInstanceServer::designerSupport() const
{
    return m_designerSupport;
}

void Qt5NodeInstanceServer::createScene(const CreateSceneCommand &command)
{
    m_designerSupport = new DesignerSupport;
    NodeInstanceServer::createScene(command);
}

void Qt5NodeInstanceServer::clearScene(const ClearSceneCommand &command)
{
    delete m_designerSupport;
    m_designerSupport = 0;
    NodeInstanceServer::clearScene(command);
}

} // QmlDesigner
<commit_msg>QmlDesigner.NodeInstances: Use new designer support interface<commit_after>/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "qt5nodeinstanceserver.h"

#include <QQuickItem>
#include <QQuickView>

#include <designersupport.h>
#include <addimportcontainer.h>
#include <createscenecommand.h>

namespace QmlDesigner {

Qt5NodeInstanceServer::Qt5NodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient)
    : NodeInstanceServer(nodeInstanceClient),
      m_designerSupport(new DesignerSupport)
{
    addImportString("import QtQuick 2.0\n");
    DesignerSupport::activateDesignerMode();
}

Qt5NodeInstanceServer::~Qt5NodeInstanceServer()
{
    delete quickView();
    delete m_designerSupport;
    m_designerSupport = 0;
}

QQuickView *Qt5NodeInstanceServer::quickView() const
{
    return m_quickView.data();
}

void Qt5NodeInstanceServer::initializeView(const QVector<AddImportContainer> &/*importVector*/)
{
    Q_ASSERT(!quickView());

    m_quickView = new QQuickView;
    DesignerSupport::createOpenGLContext(m_quickView.data());
}

QQmlView *Qt5NodeInstanceServer::declarativeView() const
{
    return 0;
}

QQmlEngine *Qt5NodeInstanceServer::engine() const
{
    if (quickView())
        return quickView()->engine();

    return 0;
}

void Qt5NodeInstanceServer::resizeCanvasSizeToRootItemSize()
{
}

void Qt5NodeInstanceServer::resetAllItems()
{
    foreach (QQuickItem *item, allItems())
        DesignerSupport::resetDirty(item);
}

QList<ServerNodeInstance> Qt5NodeInstanceServer::setupScene(const CreateSceneCommand &command)
{
    setupFileUrl(command.fileUrl());
    setupImports(command.imports());
    setupDummyData(command.fileUrl());

    QList<ServerNodeInstance> instanceList = setupInstances(command);

    quickView()->resize(rootNodeInstance().boundingRect().size().toSize());

    return instanceList;
}

QList<QQuickItem*> subItems(QQuickItem *parentItem)
{
    QList<QQuickItem*> itemList;
    itemList.append(parentItem->childItems());

    foreach (QQuickItem *childItem, parentItem->childItems())
        itemList.append(subItems(childItem));

    return itemList;
}

QList<QQuickItem*> Qt5NodeInstanceServer::allItems() const
{
    QList<QQuickItem*> itemList;

    if (quickView()) {
        itemList.append(quickView()->rootObject());
        itemList.append(subItems(quickView()->rootObject()));
    }

    return itemList;
}

void Qt5NodeInstanceServer::refreshBindings()
{
    DesignerSupport::refreshExpressions(context());
}

DesignerSupport *Qt5NodeInstanceServer::designerSupport() const
{
    return m_designerSupport;
}

void Qt5NodeInstanceServer::createScene(const CreateSceneCommand &command)
{
    m_designerSupport = new DesignerSupport;
    NodeInstanceServer::createScene(command);
}

void Qt5NodeInstanceServer::clearScene(const ClearSceneCommand &command)
{
    delete m_designerSupport;
    m_designerSupport = 0;
    NodeInstanceServer::clearScene(command);
}

} // QmlDesigner
<|endoftext|>