<commit_before>#include "DTE.h"
#include "URC.h"

void DTE::debugPrint(const char message[], bool returnChar) {
	if(debug) {
		Serial.print(message);
		if(returnChar) Serial.println();
	}
}

void DTE::debugPrint(const __FlashStringHelper *message, bool returnChar) {
	char buffer[strlen_P((const char *)message)+1];
	strcpy_P(buffer, (const char *)message);
	debugPrint(buffer, returnChar);
}

bool DTE::atReIssueLastCommand(void) {
  const __FlashStringHelper *command = F("A/\r");
	char buffer[4]; // "A/\r"

	clearReceivedBuffer();
  if(!ATCommand(command)) return false;
	if(!ATResponseOk()) return false;
	return true;
}

bool DTE::atSetCommandEchoMode(bool echo) {
  const __FlashStringHelper *command = F("ATE%d&W\r");
	char buffer[8]; // "ATEX&W\r"

	sprintf_P(buffer, (const char *)command, echo?1:0);

	clearReceivedBuffer();
  if(!ATCommand(buffer)) return false;
	if(!ATResponseOk()) return false;
  this->echo = echo;
	return true;
}

bool DTE::atSetLocalDataFlowControl(void) {
	const __FlashStringHelper *command = F("AT+IFC?\r");
	const __FlashStringHelper *response = F("+IFC: ");
  struct FlowControl flowControl;

  flowControl = this->flowControl;
	clearReceivedBuffer();
  if(!ATCommand(command)) return false;
	if(!ATResponseContain(response)) return false;
	char *pointer = strstr_P(getResponse(), (const char *)response) + strlen_P((const char *)response);
  char *str = strtok(pointer, ",");
  for (size_t i = 0; i < 2 && str != NULL; i++) {
    if (i == 0) flowControl.dce = str[0] - '0';
    if (i == 1) flowControl.dte = str[0] - '0';
    str = strtok(NULL, ",");
  }
  if(!ATResponseOk()) return false;
	this->flowControl = flowControl;
	return true;
}

bool DTE::atSetLocalDataFlowControl(unsigned char dce, unsigned char dte) {
  const __FlashStringHelper *command = F("AT+IFC=%d,%d;&W\r");
	char buffer[15]; // "AT+IFC=X,X;&W\r"

	sprintf_P(buffer, (const char *)command, dce, dte);

	clearReceivedBuffer();
  if(!ATCommand(buffer)) return false;
	if(!ATResponseOk()) return false;
  flowControl.dce = dce;
  flowControl.dte = dte;
	return true;
}

bool DTE::atSetFixedLocalRate(void) {
	const __FlashStringHelper *command = F("AT+IPR?\r");
	const __FlashStringHelper *response = F("+IPR: ");
  long baudrate = 0;

	clearReceivedBuffer();
  if(!ATCommand(command)) return false;
	if(!ATResponseContain(response)) return false;
	char *str = strstr_P(getResponse(), (const char *)response) + strlen_P((const char *)response);
  baudrate = atoi(str);
  if(!ATResponseOk()) return false;
	this->baudrate = baudrate;
	return true;
}

bool DTE::atSetFixedLocalRate(long baudrate) {
  const __FlashStringHelper *command = F("AT+IPR=%ld\r");
	char buffer[15]; // "AT+IPR=XXXXXX\r"

	sprintf_P(buffer, (const char *)command, baudrate);

	clearReceivedBuffer();
  if(!ATCommand(buffer)) return false;
	if(!ATResponseOk()) return false;
  this->baudrate = baudrate;
	return true;
}

/* DTE Class */
DTE::DTE(HardwareSerial &hardwareSerial, int powerPin, bool debug) {
	this->hardwareSerial = &hardwareSerial;
	this->powerPin = powerPin;
	this->debug = debug;

	pinMode(this->powerPin, OUTPUT);
	digitalWrite(this->powerPin, LOW);
}

DTE::DTE(SoftwareSerial &softwareSerial, int powerPin, bool debug) {
	this->softwareSerial = &softwareSerial;
	this->powerPin = powerPin;
	this->debug = debug;

	pinMode(this->powerPin, OUTPUT);
	digitalWrite(this->powerPin, LOW);
}

int DTE::available(void) {
	if(hardwareSerial) return hardwareSerial->available();
	if(softwareSerial) return softwareSerial->available();
	return -1;
}

bool DTE::isListening(void) {
	if(softwareSerial) return softwareSerial->isListening();
	return false;
}

bool DTE::listen(void) {
	if(softwareSerial) return softwareSerial->listen();
	return false;
}

size_t DTE::write(const char str[]) {
	if(hardwareSerial) return hardwareSerial->write(str);
	if(softwareSerial) return softwareSerial->write(str);
	return 0;
}

size_t DTE::readBytes(char buffer[], size_t length) {
	if(hardwareSerial) return hardwareSerial->readBytes(buffer, length);
	if(softwareSerial) return softwareSerial->readBytes(buffer, length);
	return 0;
}

void DTE::togglePower(void) {
	debugPrint(F("Toggle Power"), true);

	digitalWrite(powerPin, HIGH);
	delay(1200);
	digitalWrite(powerPin, LOW);
	powerDown = false;
	while(ATResponse(3000)) {
		if(isResponseEqual(F("RDY"))) {
			powerDown = false;
			Urc.resetUnsolicitedResultCode();
			if(AT()) return;
		}
		if(isResponseEqual(F("NORMAL POWER DOWN"))) {
			powerDown = true;
			delay(1000);
			return;
		}
		else {
			unsolicitedResultCode();
		}
	}
	if (AT()) {
		powerDown = false;
		Urc.resetUnsolicitedResultCode();
		return;
	}
	else {
		powerDown = true;
		delay(1000);
		return;
	}
}

void DTE::clearReceivedBuffer(void) {
	debugPrint(F("clearReceivedBuffer"), true);
  setFlowControlStatusDce(true);
  unsigned long t = millis();
	if(hardwareSerial) {
    while(hardwareSerial->available() == 0 && millis() - t < 50);
		if(hardwareSerial->available() == 0) {
		  setFlowControlStatusDce(false);
			return;
		}
	}
	if(softwareSerial) {
    while(softwareSerial->available() == 0 && millis() - t < 50);
		if(softwareSerial->available() == 0) {
		  setFlowControlStatusDce(false);
			return;
		}
	}
	while (ATResponse(50)) {
		if(isResponseEqual("RDY")) echo = true;
		else unsolicitedResultCode();
	}
  setFlowControlStatusDce(false);
}

bool DTE::AT(void) {
  const __FlashStringHelper *command = F("AT\r");

	powerDown = false;
	echo = false;
	ATCommand(command);
	while(true) {
		if(!ATResponse()) {
			powerDown = true;
			break;
		}
		if(isResponseEqual(command)) echo = true;
		else if(isResponseOk()) return true;
		else unsolicitedResultCode();
	}
  powerDown = true;
	return false;
}

bool DTE::ATCommand(const char at[]) {
	debugPrint("Command: ");
	debugPrint(at, true);

  setFlowControlStatusDce(false);
	if(hardwareSerial) hardwareSerial->print(at);
	if(softwareSerial) softwareSerial->print(at);
	if(echo) {
    if(strlen(at) > (sizeof(response)-2)) {
      char atEcho[strlen(at) + 3];
      while (true) {
        if (!ATResponse(atEcho, sizeof(atEcho))) return false;
        if (strstr(atEcho, "ERROR") != NULL) return false;
        if (strcmp(atEcho, at) == 0) break;
        else Urc.unsolicitedResultCode(atEcho);
      }
    }
    else if(!ATResponseEqual(at)) return false;
  }
	return true;
}

bool DTE::ATCommand(const __FlashStringHelper *at) {
	char buffer[strlen_P((const char *)at)+1];
	strcpy_P(buffer, (const char *)at);
	return ATCommand(buffer);
}

bool DTE::ATResponse(char buffer[], size_t bufferSize, unsigned long timeout) {
	if(powerDown) {
		debugPrint(F("Power Down"), true);
		return false;
	}

  setFlowControlStatusDce(true);

	unsigned long t = millis();
	while(true) {
		if(hardwareSerial) {
			if (hardwareSerial->available() > 0) break;
		}
		if(softwareSerial) {
			if (softwareSerial->available() > 0) break;
		}
		if(millis() - t > timeout) {
			debugPrint(F("No response"), true);
			return false;
		}
	}

	t = millis();
	unsigned int i = 0;
	while(true) {
		if(hardwareSerial) {
			while (hardwareSerial->available() > 0) {
				buffer[i++] = hardwareSerial->read();
				t = millis();
        if(i >= 2) {
    			if(buffer[i-2] == '\r' && buffer[i-1] == '\n') break;
        }
			}
		}
		if(softwareSerial) {
			while (softwareSerial->available() > 0) {
				buffer[i++] = softwareSerial->read();
				t = millis();
        if(i >= 2) {
    			if(buffer[i-2] == '\r' && buffer[i-1] == '\n') break;
        }
			}
		}
		if(i >= 2) {
			if(buffer[i-2] == '\r' && buffer[i-1] == '\n') {
				buffer[i-2] = '\0';
				if(strlen(buffer) > 0) break;
				else i = 0;
			}
		}
		if(millis() - t > 50 || i >= (bufferSize-1)) {
			buffer[i] = '\0';
			break;
		}
	}
	debugPrint(F("Response: "));
	debugPrint(buffer, true);
	return true;
}

bool DTE::ATResponse(unsigned long timeout) {
  return ATResponse(response, sizeof(response), timeout);
}

bool DTE::ATResponseEqual(const char expected[], unsigned long timeout) {
	while(true) {
		if(!ATResponse(timeout)) return false;
		if(isResponseContain(F("ERROR"))) return false;
		if(isResponseEqual(expected)) break;
		if(isResponseEqual(F("RDY"))) echo = true;
		else unsolicitedResultCode();
	}
	return true;
}

bool DTE::ATResponseEqual(const __FlashStringHelper *expected, unsigned long timeout) {
	char buffer[strlen_P((const char *)expected)+1];
	strcpy_P(buffer, (const char *)expected);
	return ATResponseEqual(buffer, timeout);
}

bool DTE::ATResponseContain(const char expected[], unsigned long timeout) {
	while(true) {
		if(!ATResponse(timeout)) return false;
		if(isResponseContain(F("ERROR"))) return false;
		if(isResponseContain(expected)) break;
		if(isResponseEqual(F("RDY"))) echo = true;
		else unsolicitedResultCode();
	}
	return true;
}

bool DTE::ATResponseContain(const __FlashStringHelper *expected, unsigned long timeout) {
	char buffer[strlen_P((const char *)expected)+1];
	strcpy_P(buffer, (const char *)expected);
	return ATResponseContain(buffer, timeout);
}

bool DTE::ATResponseOk(unsigned long timeout) {
  if(!ATResponseEqual(F("OK"), timeout)) return false;
  setFlowControlStatusDce(false);
	return true;
}

bool DTE::isResponseEqual(const char expected[]) {
	if(strcmp(response, expected) != 0) return false;
	return true;
}

bool DTE::isResponseEqual(const __FlashStringHelper *expected) {
	char buffer[strlen_P((const char *)expected)+1];
	strcpy_P(buffer, (const char *)expected);
	return isResponseEqual(buffer);
}

bool DTE::isResponseContain(const char expected[]) {
	if(strstr(response, expected) == NULL) return false;
	return true;
}

bool DTE::isResponseContain(const __FlashStringHelper *expected) {
	char buffer[strlen_P((const char *)expected)+1];
	strcpy_P(buffer, (const char *)expected);
	return isResponseContain(buffer);
}

bool DTE::isResponseOk(void) {
  if(!isResponseContain(F("OK"))) return false;
  setFlowControlStatusDce(false);
	return true;
}

bool DTE::unsolicitedResultCode(void) {
	debugPrint(F("URC: "));
	debugPrint(response, true);
  return Urc.unsolicitedResultCode(response);
}

bool DTE::setEcho(bool echo) {
	if (this->echo == echo) return true;
	if (!atSetCommandEchoMode(echo)) return false;
	return true;
}

struct FlowControl DTE::getFlowControl(void) {
  if(!atSetLocalDataFlowControl()) return FlowControl{};
  return this->flowControl;
}

bool DTE::setFlowControl(unsigned char dce, unsigned char dte) {
  if(!atSetLocalDataFlowControl(dce, dte)) return false;
  if(flowControl.dce == 1) setFlowControlStatusDce(false);
  return true;
}

bool DTE::setFlowControlStatusDce(bool on) {
  if (flowControl.dce != 1) return false;
  if (flowControl.dceOn == on) return true;
  if (hardwareSerial) hardwareSerial->write(on?17:19);
  if (softwareSerial) softwareSerial->write(on?17:19);
  flowControl.dceOn = on;
  return true;
}

long DTE::getBaudrate(void) {
  if(!atSetLocalDataFlowControl()) return 0;
  return baudrate;
}

bool DTE::powerReset(void) {
	togglePower();
	while (powerDown) {
		togglePower();
	}
	powerDown = false;
	if(isEcho())
		setEcho(false);
	if(getFlowControl().dce == 0)
  	setFlowControl(1, 0);
	setFlowControlStatusDce(false);
	return true;
}
<commit_msg>Check buffer after OK, make sure no data is lost<commit_after>#include "DTE.h"
#include "URC.h"

void DTE::debugPrint(const char message[], bool returnChar) {
	if(debug) {
		Serial.print(message);
		if(returnChar) Serial.println();
	}
}

void DTE::debugPrint(const __FlashStringHelper *message, bool returnChar) {
	char buffer[strlen_P((const char *)message)+1];
	strcpy_P(buffer, (const char *)message);
	debugPrint(buffer, returnChar);
}

bool DTE::atReIssueLastCommand(void) {
  const __FlashStringHelper *command = F("A/\r");
	char buffer[4]; // "A/\r"

	clearReceivedBuffer();
  if(!ATCommand(command)) return false;
	if(!ATResponseOk()) return false;
	return true;
}

bool DTE::atSetCommandEchoMode(bool echo) {
  const __FlashStringHelper *command = F("ATE%d&W\r");
	char buffer[8]; // "ATEX&W\r"

	sprintf_P(buffer, (const char *)command, echo?1:0);

	clearReceivedBuffer();
  if(!ATCommand(buffer)) return false;
	if(!ATResponseOk()) return false;
  this->echo = echo;
	return true;
}

bool DTE::atSetLocalDataFlowControl(void) {
	const __FlashStringHelper *command = F("AT+IFC?\r");
	const __FlashStringHelper *response = F("+IFC: ");
  struct FlowControl flowControl;

  flowControl = this->flowControl;
	clearReceivedBuffer();
  if(!ATCommand(command)) return false;
	if(!ATResponseContain(response)) return false;
	char *pointer = strstr_P(getResponse(), (const char *)response) + strlen_P((const char *)response);
  char *str = strtok(pointer, ",");
  for (size_t i = 0; i < 2 && str != NULL; i++) {
    if (i == 0) flowControl.dce = str[0] - '0';
    if (i == 1) flowControl.dte = str[0] - '0';
    str = strtok(NULL, ",");
  }
  if(!ATResponseOk()) return false;
	this->flowControl = flowControl;
	return true;
}

bool DTE::atSetLocalDataFlowControl(unsigned char dce, unsigned char dte) {
  const __FlashStringHelper *command = F("AT+IFC=%d,%d;&W\r");
	char buffer[15]; // "AT+IFC=X,X;&W\r"

	sprintf_P(buffer, (const char *)command, dce, dte);

	clearReceivedBuffer();
  if(!ATCommand(buffer)) return false;
	if(!ATResponseOk()) return false;
  flowControl.dce = dce;
  flowControl.dte = dte;
	return true;
}

bool DTE::atSetFixedLocalRate(void) {
	const __FlashStringHelper *command = F("AT+IPR?\r");
	const __FlashStringHelper *response = F("+IPR: ");
  long baudrate = 0;

	clearReceivedBuffer();
  if(!ATCommand(command)) return false;
	if(!ATResponseContain(response)) return false;
	char *str = strstr_P(getResponse(), (const char *)response) + strlen_P((const char *)response);
  baudrate = atoi(str);
  if(!ATResponseOk()) return false;
	this->baudrate = baudrate;
	return true;
}

bool DTE::atSetFixedLocalRate(long baudrate) {
  const __FlashStringHelper *command = F("AT+IPR=%ld\r");
	char buffer[15]; // "AT+IPR=XXXXXX\r"

	sprintf_P(buffer, (const char *)command, baudrate);

	clearReceivedBuffer();
  if(!ATCommand(buffer)) return false;
	if(!ATResponseOk()) return false;
  this->baudrate = baudrate;
	return true;
}

/* DTE Class */
DTE::DTE(HardwareSerial &hardwareSerial, int powerPin, bool debug) {
	this->hardwareSerial = &hardwareSerial;
	this->powerPin = powerPin;
	this->debug = debug;

	pinMode(this->powerPin, OUTPUT);
	digitalWrite(this->powerPin, LOW);
}

DTE::DTE(SoftwareSerial &softwareSerial, int powerPin, bool debug) {
	this->softwareSerial = &softwareSerial;
	this->powerPin = powerPin;
	this->debug = debug;

	pinMode(this->powerPin, OUTPUT);
	digitalWrite(this->powerPin, LOW);
}

int DTE::available(void) {
	if(hardwareSerial) return hardwareSerial->available();
	if(softwareSerial) return softwareSerial->available();
	return -1;
}

bool DTE::isListening(void) {
	if(softwareSerial) return softwareSerial->isListening();
	return false;
}

bool DTE::listen(void) {
	if(softwareSerial) return softwareSerial->listen();
	return false;
}

size_t DTE::write(const char str[]) {
	if(hardwareSerial) return hardwareSerial->write(str);
	if(softwareSerial) return softwareSerial->write(str);
	return 0;
}

size_t DTE::readBytes(char buffer[], size_t length) {
	if(hardwareSerial) return hardwareSerial->readBytes(buffer, length);
	if(softwareSerial) return softwareSerial->readBytes(buffer, length);
	return 0;
}

void DTE::togglePower(void) {
	debugPrint(F("Toggle Power"), true);

	digitalWrite(powerPin, HIGH);
	delay(1200);
	digitalWrite(powerPin, LOW);
	powerDown = false;
	while(ATResponse(3000)) {
		if(isResponseEqual(F("RDY"))) {
			powerDown = false;
			Urc.resetUnsolicitedResultCode();
			if(AT()) return;
		}
		if(isResponseEqual(F("NORMAL POWER DOWN"))) {
			powerDown = true;
			delay(1000);
			return;
		}
		else {
			unsolicitedResultCode();
		}
	}
	if (AT()) {
		powerDown = false;
		Urc.resetUnsolicitedResultCode();
		return;
	}
	else {
		powerDown = true;
		delay(1000);
		return;
	}
}

void DTE::clearReceivedBuffer(void) {
	debugPrint(F("clearReceivedBuffer"), true);
  setFlowControlStatusDce(true);
  unsigned long t = millis();
	if(hardwareSerial) {
    while(hardwareSerial->available() == 0 && millis() - t < 50);
		if(hardwareSerial->available() == 0) {
		  setFlowControlStatusDce(false);
			return;
		}
	}
	if(softwareSerial) {
    while(softwareSerial->available() == 0 && millis() - t < 50);
		if(softwareSerial->available() == 0) {
		  setFlowControlStatusDce(false);
			return;
		}
	}
	while (ATResponse(50)) {
		if(isResponseEqual("RDY")) echo = true;
		else unsolicitedResultCode();
	}
  setFlowControlStatusDce(false);
}

bool DTE::AT(void) {
  const __FlashStringHelper *command = F("AT\r");

	powerDown = false;
	echo = false;
	ATCommand(command);
	while(true) {
		if(!ATResponse()) {
			powerDown = true;
			break;
		}
		if(isResponseEqual(command)) echo = true;
		else if(isResponseOk()) return true;
		else unsolicitedResultCode();
	}
  powerDown = true;
	return false;
}

bool DTE::ATCommand(const char at[]) {
	debugPrint("Command: ");
	debugPrint(at, true);

  setFlowControlStatusDce(false);
	if(hardwareSerial) hardwareSerial->print(at);
	if(softwareSerial) softwareSerial->print(at);
	if(echo) {
    if(strlen(at) > (sizeof(response)-2)) {
      char atEcho[strlen(at) + 3];
      while (true) {
        if (!ATResponse(atEcho, sizeof(atEcho))) return false;
        if (strstr(atEcho, "ERROR") != NULL) return false;
        if (strcmp(atEcho, at) == 0) break;
        else Urc.unsolicitedResultCode(atEcho);
      }
    }
    else if(!ATResponseEqual(at)) return false;
  }
	return true;
}

bool DTE::ATCommand(const __FlashStringHelper *at) {
	char buffer[strlen_P((const char *)at)+1];
	strcpy_P(buffer, (const char *)at);
	return ATCommand(buffer);
}

bool DTE::ATResponse(char buffer[], size_t bufferSize, unsigned long timeout) {
	if(powerDown) {
		debugPrint(F("Power Down"), true);
		return false;
	}

  setFlowControlStatusDce(true);

	unsigned long t = millis();
	while(true) {
		if(hardwareSerial) {
			if (hardwareSerial->available() > 0) break;
		}
		if(softwareSerial) {
			if (softwareSerial->available() > 0) break;
		}
		if(millis() - t > timeout) {
			debugPrint(F("No response"), true);
			return false;
		}
	}

	t = millis();
	unsigned int i = 0;
	while(true) {
		if(hardwareSerial) {
			while (hardwareSerial->available() > 0) {
				buffer[i++] = hardwareSerial->read();
				t = millis();
        if(i >= 2) {
    			if(buffer[i-2] == '\r' && buffer[i-1] == '\n') break;
        }
			}
		}
		if(softwareSerial) {
			while (softwareSerial->available() > 0) {
				buffer[i++] = softwareSerial->read();
				t = millis();
        if(i >= 2) {
    			if(buffer[i-2] == '\r' && buffer[i-1] == '\n') break;
        }
			}
		}
		if(i >= 2) {
			if(buffer[i-2] == '\r' && buffer[i-1] == '\n') {
				buffer[i-2] = '\0';
				if(strlen(buffer) > 0) break;
				else i = 0;
			}
		}
		if(millis() - t > 50 || i >= (bufferSize-1)) {
			buffer[i] = '\0';
			break;
		}
	}
	debugPrint(F("Response: "));
	debugPrint(buffer, true);
	return true;
}

bool DTE::ATResponse(unsigned long timeout) {
  return ATResponse(response, sizeof(response), timeout);
}

bool DTE::ATResponseEqual(const char expected[], unsigned long timeout) {
	while(true) {
		if(!ATResponse(timeout)) return false;
		if(isResponseContain(F("ERROR"))) return false;
		if(isResponseEqual(expected)) break;
		if(isResponseEqual(F("RDY"))) echo = true;
		else unsolicitedResultCode();
	}
	return true;
}

bool DTE::ATResponseEqual(const __FlashStringHelper *expected, unsigned long timeout) {
	char buffer[strlen_P((const char *)expected)+1];
	strcpy_P(buffer, (const char *)expected);
	return ATResponseEqual(buffer, timeout);
}

bool DTE::ATResponseContain(const char expected[], unsigned long timeout) {
	while(true) {
		if(!ATResponse(timeout)) return false;
		if(isResponseContain(F("ERROR"))) return false;
		if(isResponseContain(expected)) break;
		if(isResponseEqual(F("RDY"))) echo = true;
		else unsolicitedResultCode();
	}
	return true;
}

bool DTE::ATResponseContain(const __FlashStringHelper *expected, unsigned long timeout) {
	char buffer[strlen_P((const char *)expected)+1];
	strcpy_P(buffer, (const char *)expected);
	return ATResponseContain(buffer, timeout);
}

bool DTE::ATResponseOk(unsigned long timeout) {
	if(!ATResponseEqual(F("OK"), timeout)) return false;
	if(available()) clearReceivedBuffer();
	setFlowControlStatusDce(false);
	return true;
}

bool DTE::isResponseEqual(const char expected[]) {
	if(strcmp(response, expected) != 0) return false;
	return true;
}

bool DTE::isResponseEqual(const __FlashStringHelper *expected) {
	char buffer[strlen_P((const char *)expected)+1];
	strcpy_P(buffer, (const char *)expected);
	return isResponseEqual(buffer);
}

bool DTE::isResponseContain(const char expected[]) {
	if(strstr(response, expected) == NULL) return false;
	return true;
}

bool DTE::isResponseContain(const __FlashStringHelper *expected) {
	char buffer[strlen_P((const char *)expected)+1];
	strcpy_P(buffer, (const char *)expected);
	return isResponseContain(buffer);
}

bool DTE::isResponseOk(void) {
  if(!isResponseContain(F("OK"))) return false;
  setFlowControlStatusDce(false);
	return true;
}

bool DTE::unsolicitedResultCode(void) {
	debugPrint(F("URC: "));
	debugPrint(response, true);
  return Urc.unsolicitedResultCode(response);
}

bool DTE::setEcho(bool echo) {
	if (this->echo == echo) return true;
	if (!atSetCommandEchoMode(echo)) return false;
	return true;
}

struct FlowControl DTE::getFlowControl(void) {
  if(!atSetLocalDataFlowControl()) return FlowControl{};
  return this->flowControl;
}

bool DTE::setFlowControl(unsigned char dce, unsigned char dte) {
  if(!atSetLocalDataFlowControl(dce, dte)) return false;
  if(flowControl.dce == 1) setFlowControlStatusDce(false);
  return true;
}

bool DTE::setFlowControlStatusDce(bool on) {
  if (flowControl.dce != 1) return false;
  if (flowControl.dceOn == on) return true;
  if (hardwareSerial) hardwareSerial->write(on?17:19);
  if (softwareSerial) softwareSerial->write(on?17:19);
  flowControl.dceOn = on;
  return true;
}

long DTE::getBaudrate(void) {
  if(!atSetLocalDataFlowControl()) return 0;
  return baudrate;
}

bool DTE::powerReset(void) {
	togglePower();
	while (powerDown) {
		togglePower();
	}
	powerDown = false;
	if(isEcho())
		setEcho(false);
	if(getFlowControl().dce == 0)
  	setFlowControl(1, 0);
	setFlowControlStatusDce(false);
	return true;
}
<|endoftext|>


<commit_before>/*
 Iot.cpp - construct messages to be sent to at&t:s m2x iot cloud
 Teppo Kallio, 2014
*/

#include <stdio.h>
#include "Iot.h"
#include "M2XStreamClient.h"

Iot::Iot(M2XStreamClient *m2xsc)
{
  _m2xsc = m2xsc;
  return;
}

boolean Iot::setBufferSizes(int s)
{
  _streamNames=(const char **) malloc(sizeof(char *) * s);
  _counts=(int * ) malloc(sizeof(int) * s);
  _ats=(const char **) malloc(sizeof(char *) * s * 2);
  _values=(double *) malloc(sizeof(double) * s * 2);
  if (_streamNames==NULL || _counts==NULL || _ats==NULL || _values==NULL) {
    Serial.println("malloc failed");
    return false;
  }
  _buffSizes=s;
  return true;
}

void Iot::reset()
{
  memset(_counts,0,sizeof(int) * _buffSizes);
  memset(_values,0,sizeof(double) * _buffSizes * 2);
  _pos=0;
  _samplecnt=0;
}

void Iot::name(char *name)
{
  _streamNames[_pos]=name;
}

void Iot::addValue(float value,char *ts)
{
  if (_samplecnt >= _buffSizes * 2) {
    Serial.println("_samplecnt overflow");
    return;
  }
  _values[_samplecnt]=value;
  _counts[_pos]++;
  _ats[_samplecnt]=ts;
  _samplecnt++;
}

void Iot::next()
{
  if (_pos < _buffSizes)
    _pos++;
  else {
    Serial.println("_pos overflow");
    return;
  }
}

int Iot::getRecCnt()
{
  return _samplecnt;
}

int Iot::send(char *id)
{
  return _m2xsc->postMultiple(id, _pos, _streamNames,
                              _counts, _ats, _values);
}

void Iot::showCounters()
{
  Serial.print("counters: ");
  for (int i=0;i<_pos;i++) {
    Serial.print(_counts[i]);
    Serial.print(" ");
  }
  Serial.println();
}

void Iot::showStreamnames()
{
  Serial.print("Streamnames: ");
  for (int i=0;i<_pos;i++) {
    Serial.print(_streamNames[i]);
    Serial.print(" ");
  }
  Serial.println();
}

void Iot::showTimes()
{
  Serial.print("Timestamps: ");
  for (int i=0;i<_samplecnt;i++)  {
    Serial.print(_ats[i]);
    Serial.print(" ");
  }
  Serial.println();
}

void Iot::showValues()
{
  Serial.print("Values: ");
  for (int i=0;i<_samplecnt;i++) {
    Serial.print(_values[i]);
    Serial.print(" ");
  }
  Serial.println();
}

<commit_msg>[upd] Tiny update to be compatible with newer M2X library.<commit_after>/*
 Iot.cpp - construct messages to be sent to at&t:s m2x iot cloud
 Teppo Kallio, 2014
*/

#include <stdio.h>
#include "Iot.h"
#include "M2XStreamClient.h"

Iot::Iot(M2XStreamClient *m2xsc)
{
  _m2xsc = m2xsc;
  return;
}

boolean Iot::setBufferSizes(int s)
{
  _streamNames=(const char **) malloc(sizeof(char *) * s);
  _counts=(int * ) malloc(sizeof(int) * s);
  _ats=(const char **) malloc(sizeof(char *) * s * 2);
  _values=(double *) malloc(sizeof(double) * s * 2);
  if (_streamNames==NULL || _counts==NULL || _ats==NULL || _values==NULL) {
    Serial.println("malloc failed");
    return false;
  }
  _buffSizes=s;
  return true;
}

void Iot::reset()
{
  memset(_counts,0,sizeof(int) * _buffSizes);
  memset(_values,0,sizeof(double) * _buffSizes * 2);
  _pos=0;
  _samplecnt=0;
}

void Iot::name(char *name)
{
  _streamNames[_pos]=name;
}

void Iot::addValue(float value,char *ts)
{
  if (_samplecnt >= _buffSizes * 2) {
    Serial.println("_samplecnt overflow");
    return;
  }
  _values[_samplecnt]=value;
  _counts[_pos]++;
  _ats[_samplecnt]=ts;
  _samplecnt++;
}

void Iot::next()
{
  if (_pos < _buffSizes)
    _pos++;
  else {
    Serial.println("_pos overflow");
    return;
  }
}

int Iot::getRecCnt()
{
  return _samplecnt;
}

int Iot::send(char *id)
{
  return _m2xsc->postDeviceUpdates(id, _pos, _streamNames,
                              _counts, _ats, _values);
}

void Iot::showCounters()
{
  Serial.print("counters: ");
  for (int i=0;i<_pos;i++) {
    Serial.print(_counts[i]);
    Serial.print(" ");
  }
  Serial.println();
}

void Iot::showStreamnames()
{
  Serial.print("Streamnames: ");
  for (int i=0;i<_pos;i++) {
    Serial.print(_streamNames[i]);
    Serial.print(" ");
  }
  Serial.println();
}

void Iot::showTimes()
{
  Serial.print("Timestamps: ");
  for (int i=0;i<_samplecnt;i++)  {
    Serial.print(_ats[i]);
    Serial.print(" ");
  }
  Serial.println();
}

void Iot::showValues()
{
  Serial.print("Values: ");
  for (int i=0;i<_samplecnt;i++) {
    Serial.print(_values[i]);
    Serial.print(" ");
  }
  Serial.println();
}

<|endoftext|>


<commit_before>#include "Log.h"
#include <boost/date_time/posix_time/posix_time.hpp>

Log * g_Log = nullptr;

static const char * g_LogLevelStr[eNumLogLevels] =
{
	"error", // eLogError
	"warn",  // eLogWarning
	"info",  // eLogInfo
	"debug"	 // eLogDebug
};

void LogMsg::Process()
{
	output << boost::posix_time::second_clock::local_time().time_of_day () <<
		"/" << g_LogLevelStr[level] << " - ";
	output << s.str();
}

void Log::Flush ()
{
#ifndef _WIN32
	if (m_LogFile)
		m_LogFile->flush();
#endif
// TODO: find out what's wrong with flush for Windows
}

void Log::SetLogFile (const std::string& fullFilePath)
{
	if (m_LogFile) delete m_LogFile;
	m_LogFile = new std::ofstream (fullFilePath, std::ofstream::out | std::ofstream::binary | std::ofstream::trunc);
	if (m_LogFile->is_open ())
		LogPrint("Logging to file ",  fullFilePath, " enabled.");
	else
	{
		delete m_LogFile;
		m_LogFile = nullptr;
	}
}
<commit_msg>Revert "fixed typo" and "fixed crash for Windows"<commit_after>#include "Log.h"
#include <boost/date_time/posix_time/posix_time.hpp>

Log * g_Log = nullptr;

static const char * g_LogLevelStr[eNumLogLevels] =
{
	"error", // eLogError
	"warn",  // eLogWarning
	"info",  // eLogInfo
	"debug"	 // eLogDebug
};

void LogMsg::Process()
{
	output << boost::posix_time::second_clock::local_time().time_of_day () <<
		"/" << g_LogLevelStr[level] << " - ";
	output << s.str();
}

void Log::Flush ()
{
	if (m_LogFile)
		m_LogFile->flush();
}

void Log::SetLogFile (const std::string& fullFilePath)
{
	if (m_LogFile) delete m_LogFile;
	m_LogFile = new std::ofstream (fullFilePath, std::ofstream::out | std::ofstream::binary | std::ofstream::trunc);
	if (m_LogFile->is_open ())
		LogPrint("Logging to file ",  fullFilePath, " enabled.");
	else
	{
		delete m_LogFile;
		m_LogFile = nullptr;
	}
}
<|endoftext|>


<commit_before>#include "PID.h"

/**
 * A PID Controller is a method of system control in which a correctional output
 * is generated to guide the system toward a desired setpoint (aka target).
 * The PID Controller calculates the output based on the following factors:
 *
 *    Gains (proportional, integral, and derivative)
 *    Target
 *    Feedback
 *
 * The gain values act as multipliers for their corresponding components of PID
 * (more detail later).  The target is the value which the system strives to
 * reach by manipulating the output.  The feedback is the system's actual
 * position or status in regards to the physical world.
 * Another important term in PID is "error", which refers to the difference
 * between the target and the feedback.
 *
 * Each of the three components of PID contributes a unique behavior to the
 * system.
 *
 *    The Proportional component introduces a linear relationship between the
 *    error (target minus feedback) and the output.  This means that as the
 *    feedback grows further away from the target, the output grows
 *    proportionally stronger.
 *
 *        Proportional component = (P Gain) * (target - feedback)
 *
 *    The Integral component is designed to give a very precise approach of the
 *    feedback to the target.  Depending on the scale of the physical system
 *    and the precision of feedback (e.g. sensors), the proportional component
 *    alone is likely not sufficient to provide adequate power (e.g. to motors)
 *    to guide the system in regards to small-scale correction.  The Integral
 *    component integrates the error of the system (target - feedback) over
 *    time.  If the system reaches a point where it is close but not exactly
 *    on top of the target, the integration will slowly build until it is
 *    powerful enough to overcome static resistances and move the system
 *    preciesly to the target.
 *
 *        Integral component = (I Gain) * Integral of error over time
 *
 *          *In this implementation, Integral is calculated with a running
 *          summation of the system's error, updated at each tick.
 *
 *    The Derivative component measures the rate of change of the feedback.
 *    It can reduce the strength of the output if the feedback is approaching
 *    the target too quickly or if the feedback is moving away from the target.
 *
 *        Derivative component = (D Gain) * ((error - lastError) / time - lastTime)
 */
PIDController::PIDController(double p, double i, double d, int (*pidSource)(), void (*pidOutput)(int output))
{
  _p = p;
  _i = i;
  _d = d;
  target = 0;
  output = 0;
  currentFeedback = 0;
  lastFeedback = 0;
  lastError = 0;
  currentTime = 0L;
  lastTime = 0L;
  integralCumulation = 0;
  maxCumulation = 30000;
  inputBounded = false;
  outputBounded = false;
  inputLowerBound = 0;
  inputUpperBound = 0;
  outputBounded = false;
  outputLowerBound = 0;
  outputUpperBound = 0;
  timeFunctionRegistered = false;
  _pidSource = pidSource;
  _pidOutput = pidOutput;
}

/**
 * This method uses the established function pointers to retrieve system
 * feedback, calculate the PID output, and deliver the correction value
 * to the parent of this PIDController.  This method should be run as
 * fast as the source of the feedback in order to provide the highest
 * resolution of control (for example, to be placed in the loop() method).
 */
void PIDController::tick()
{
  //Retrieve system feedback from user callback.
  currentFeedback = _pidSource();

  //Apply input bounds if necessary.
  if(inputBounded)
  {
    if(currentFeedback > inputUpperBound) currentFeedback = inputUpperBound;
    if(currentFeedback < inputLowerBound) currentFeedback = inputLowerBound;
  }

  //Calculate the error between the feedback and the target.
  error = target - currentFeedback;

  //If we have a registered way to retrieve the system time, use time in PID calculations.
  if(timeFunctionRegistered)
  {
    //Retrieve system time
    currentTime = _getSystemTime();

    //Calculate time since last tick() cycle.
    long deltaTime = currentTime - lastTime;

    //Calculate the integral of the feedback data since last cycle.
    int cycleIntegral = (lastError + error / 2) * deltaTime;

    //Add this cycle's integral to the integral cumulation.
    integralCumulation += cycleIntegral;

    //Calculate the slope of the line with data from the current and last cycles.
    cycleDerivative = (error - lastError) / deltaTime;

    //Save time data for next iteration.
    lastTime = currentTime;
  }
  //If we have no way to retrieve system time, estimate calculations.
  else
  {
    integralCumulation += error;
    cycleDerivative = (error - lastError);
  }

  //Prevent the integral cumulation from becoming overwhelmingly huge.
  if(integralCumulation > maxCumulation) integralCumulation = maxCumulation;
  if(integralCumulation < -maxCumulation) integralCumulation = -maxCumulation;

  //Calculate the system output based on data and PID gains.
  output = (int) ((error * _p) + (integralCumulation * _i) + (cycleDerivative * _d));

  //Save a record of this iteration's data.
  lastFeedback = currentFeedback;
  lastError = error;

  //Trim the output to the bounds if needed.
  if(outputBounded)
  {
    if(output > outputUpperBound) output = outputUpperBound;
    if(output < outputLowerBound) output = outputLowerBound;
  }

  _pidOutput(output);
}

/**
 * Sets the target of this PIDController.  This system will generate
 * correction outputs indended to guide the feedback variable (such
 * as position, velocity, etc.) toward the established target.
 */
void PIDController::setTarget(int t)
{
  target = t;
}

/**
 * Returns the current target of this PIDController.
 * @return The current target of this PIDController.
 */
int PIDController::getTarget()
{
  return target;
}

/**
 * Returns the latest output generated by this PIDController.  This value is
 * also delivered to the parent systems via the PIDOutput function pointer
 * provided in the constructor of this PIDController.
 * @return The latest output generated by this PIDController.
 */
int PIDController::getOutput()
{
  return output;
}

/**
 * Returns the last read feedback of this PIDController.
 */
int PIDController::getFeedback()
{
  return currentFeedback;
}

/**
 * Returns the value that the Proportional component is contributing to the output.
 * @return The value that the Proportional component is contributing to the output.
 */
int PIDController::getProportionalComponent()
{
  return (int) (error * _p);
}

/**
 * Returns the value that the Integral component is contributing to the output.
 * @return The value that the Integral component is contributing to the output.
 */
int PIDController::getIntegralComponent()
{
  return (int) (integralCumulation * _i);
}

/**
 * Returns the value that the Derivative component is contributing to the output.
 * @return The value that the Derivative component is contributing to the output.
 */
int PIDController::getDerivativeComponent()
{
  return (int) (cycleDerivative * _d);
}

/**
 * Sets the maximum value that the integral cumulation can reach.
 * @param max The maximum value of the integral cumulation.
 */
void PIDController::setMaxIntegralCumulation(int max)
{
  //If the new max value is less than 0, invert to make positive.
  if(max < 0)
  {
    max = -max;
  }

  //If the new max is not more than 1 then the cumulation is useless.
  if(max > 1)
  {
    maxCumulation = max;
  }
}

/**
 * Returns the maximum value that the integral value can cumulate to.
 * @return The maximum value that the integral value can cumulate to.
 */
int PIDController::getMaxIntegralCumulation()
{
  return maxCumulation;
}

/**
 * Returns the current cumulative integral value in this PIDController.
 * @return The current cumulative integral value in this PIDController.
 */
int PIDController::getIntegralCumulation()
{
  return integralCumulation;
}

/**
 * Enables or disables bounds on the input.  Bounds limit the upper and
 * lower values that this PIDController will ever accept as input.
 * Outlying values will be trimmed to the upper or lower bound as necessary.
 * @param bounded True to enable input bounds, False to disable.
 */
void PIDController::setInputBounded(bool bounded)
{
  inputBounded = bounded;
}

/**
 * Enables or disables bounds on the output.  Bounds limit the upper and lower
 * values that this PIDController will ever generate as output.
 * @param bounded True to enable output bounds, False to disable.
 */
void PIDController::setOutputBounded(bool bounded)
{
  outputBounded = bounded;
}

/**
 * Returns whether the input of this PIDController is being bounded.
 * @return True if the input of this PIDController is being bounded.
 */
bool PIDController::isInputBounded()
{
  return inputBounded;
}

/**
 * Returns whether the output of this PIDController is being bounded.
 * @return True if the output of this PIDController is being bounded.
 */
bool PIDController::isOutputBounded()
{
  return outputBounded;
}

/**
 * Sets bounds which limit the lower and upper extremes that this PIDController
 * accepts as inputs.  Outliers are trimmed to the lower and upper bounds.
 * Setting input bounds automatically enables input bounds.
 * @param lower The lower input bound.
 * @param upper The upper input bound.
 */
void PIDController::setInputBounds(int lower, int upper)
{
  if(upper > lower)
  {
    inputBounded = true;
    inputUpperBound = upper;
    inputLowerBound = lower;
  }
}

/**
 * Sets bounds which limit the lower and upper extremes that this PIDController
 * will ever generate as output.  Setting output bounds automatically enables
 * output bounds.
 * @param lower The lower output bound.
 * @param upper The upper output bound.
 */
void PIDController::setOutputBounds(int lower, int upper)
{
  if(upper > lower)
  {
    outputBounded = true;
    outputLowerBound = lower;
    outputUpperBound = upper;
  }
}

/**
 * Returns the lower input bound of this PIDController.
 * @return The lower input bound of this PIDController.
 */
int PIDController::getInputLowerBound()
{
  return inputLowerBound;
}

/**
 * Returns the upper input bound of this PIDController.
 * @return The upper input bound of this PIDController.
 */
int PIDController::getInputUpperBound()
{
  return inputUpperBound;
}

/**
 * Returns the lower output bound of this PIDController.
 * @return The lower output bound of this PIDController.
 */
int PIDController::getOutputLowerBound()
{
  return outputLowerBound;
}

/**
 * Returns the upper output bound of this PIDController.
 * @return The upper output bound of this PIDController.
 */
int PIDController::getOutputUpperBound()
{
  return outputUpperBound;
}

/**
 * Sets new values for all PID Gains.
 * @param p The new proportional gain.
 * @param i The new integral gain.
 * @param d The new derivative gain.
 */
void PIDController::setPID(double p, double i, double d)
{
  _p = p;
  _i = i;
  _d = d;
}

/**
 * Sets a new value for the proportional gain.
 * @param p The new proportional gain.
 */
void PIDController::setP(double p)
{
  _p = p;
}

/**
 * Sets a new value for the integral gain.
 * @param i The new integral gain.
 */
void PIDController::setI(double i)
{
  _i = i;
}

/**
 * Sets a new value for the derivative gain.
 * @param d The new derivative gain.
 */
void PIDController::setD(double d)
{
  _d = d;
}

/**
 * Returns the proportional gain.
 * @return The proportional gain.
 */
double PIDController::getP()
{
  return _p;
}

/**
 * Returns the integral gain.
 * @return The integral gain.
 */
double PIDController::getI()
{
  return _i;
}

/**
 * Returns the derivative gain.
 * @return The derivative gain.
 */
double PIDController::getD()
{
  return _d;
}

/**
 * Sets the function pointer to the PID Source.  A PID Source
 * is a function which returns a value to be used as the PIDController's
 * control feedback.  This value can be a reading from a sensor or other
 * data source that contains information regarding the system's actual
 * state.
 * Below is an example of using a PIDSource:
 *
 *    int pidSource()
 *    {
 *        return mySensor.getValue();
 *    }
 *    myPIDController.setPIDSource(pidSource);
 *
 * @param (*getFeedback) A function pointer that retrieves system feedback.
 */
void PIDController::setPIDSource(int (*pidSource)())
{
  _pidSource = pidSource;
}

/**
 * Sets the function pointer to the PID Output.  A PID Output
 * is a function which delivers a value to the parent system in order to guide
 * the system based on the PID loop's result.  This value can be delivered
 * directly to motors, to a variable that directs steering, or other means of
 * influencing the system.
 * Below is an example of using a PIDOutput:
 *
 *    void pidOutput(int output)
 *    {
 *        myMotor.write(output);
 *    }
 *    myPIDController.setPIDOutput(pidOutput);
 *
 * @param (*onUpdate) A function pointer that delivers system output.
 */
void PIDController::setPIDOutput(void (*pidOutput)(int output))
{
  _pidOutput = pidOutput;
}

/**
 * Use this to add a hook into the PID Controller that allows it to
 * read the system time no matter what platform this library is run
 * on.  Though developed for an arduino project, there is no code
 * tying this library to the arduino project.  To use the arduino
 * clock, however, register the time-getting function (millis())
 * like this:
 *
 *    myPIDController.registerTimeInput(millis);
 *    *Note that in this example, millis has no parentheses.
 *
 * @param (*getSystemTime) Pointer to a function that returns system time.
 */
void PIDController::registerTimeFunction(unsigned long (*getSystemTime)())
{
  _getSystemTime = getSystemTime;
  timeFunctionRegistered = true;
}
<commit_msg>Updated documentation<commit_after>#include "PID.h"

/**
 * A PID Controller is a method of system control in which a correctional output
 * is generated to guide the system toward a desired setpoint (aka target).
 * The PID Controller calculates the output based on the following factors:
 *
 *    Gains (proportional, integral, and derivative)
 *    Target
 *    Feedback
 *
 * The gain values act as multipliers for their corresponding components of PID
 * (more detail later).  The target is the value which the system strives to
 * reach by manipulating the output.  The feedback is the system's actual
 * position or status in regards to the physical world.
 * Another important term in PID is "error", which refers to the difference
 * between the target and the feedback.
 *
 * Each of the three components of PID contributes a unique behavior to the
 * system.
 *
 *    The Proportional component introduces a linear relationship between the
 *    error (target minus feedback) and the output.  This means that as the
 *    feedback grows further away from the target, the output grows
 *    proportionally stronger.
 *
 *        Proportional component = (P Gain) * (target - feedback)
 *
 *    The Integral component is designed to give a very precise approach of the
 *    feedback to the target.  Depending on the scale of the physical system
 *    and the precision of feedback (e.g. sensors), the proportional component
 *    alone is likely not sufficient to provide adequate power (e.g. to motors)
 *    to guide the system in regards to small-scale correction.  The Integral
 *    component integrates the error of the system (target - feedback) over
 *    time.  If the system reaches a point where it is close but not exactly
 *    on top of the target, the integration will slowly build until it is
 *    powerful enough to overcome static resistances and move the system
 *    preciesly to the target.
 *
 *        Integral component = (I Gain) * Integral of error over time
 *
 *          *In this implementation, Integral is calculated with a running
 *          summation of the system's error, updated at each tick.
 *
 *    The Derivative component measures the rate of change of the feedback.
 *    It can reduce the strength of the output if the feedback is approaching
 *    the target too quickly or if the feedback is moving away from the target.
 *
 *        Derivative component = (D Gain) * ((error - lastError) / time - lastTime)
 *
 * The output generated by the PID Controller is the sum of the three
 * components.
 *
 *    PID output = Proportional component + Integral component + Derivative component
 */
PIDController::PIDController(double p, double i, double d, int (*pidSource)(), void (*pidOutput)(int output))
{
  _p = p;
  _i = i;
  _d = d;
  target = 0;
  output = 0;
  currentFeedback = 0;
  lastFeedback = 0;
  lastError = 0;
  currentTime = 0L;
  lastTime = 0L;
  integralCumulation = 0;
  maxCumulation = 30000;
  inputBounded = false;
  outputBounded = false;
  inputLowerBound = 0;
  inputUpperBound = 0;
  outputBounded = false;
  outputLowerBound = 0;
  outputUpperBound = 0;
  timeFunctionRegistered = false;
  _pidSource = pidSource;
  _pidOutput = pidOutput;
}

/**
 * This method uses the established function pointers to retrieve system
 * feedback, calculate the PID output, and deliver the correction value
 * to the parent of this PIDController.  This method should be run as
 * fast as the source of the feedback in order to provide the highest
 * resolution of control (for example, to be placed in the loop() method).
 */
void PIDController::tick()
{
  //Retrieve system feedback from user callback.
  currentFeedback = _pidSource();

  //Apply input bounds if necessary.
  if(inputBounded)
  {
    if(currentFeedback > inputUpperBound) currentFeedback = inputUpperBound;
    if(currentFeedback < inputLowerBound) currentFeedback = inputLowerBound;
  }

  //Calculate the error between the feedback and the target.
  error = target - currentFeedback;

  //If we have a registered way to retrieve the system time, use time in PID calculations.
  if(timeFunctionRegistered)
  {
    //Retrieve system time
    currentTime = _getSystemTime();

    //Calculate time since last tick() cycle.
    long deltaTime = currentTime - lastTime;

    //Calculate the integral of the feedback data since last cycle.
    int cycleIntegral = (lastError + error / 2) * deltaTime;

    //Add this cycle's integral to the integral cumulation.
    integralCumulation += cycleIntegral;

    //Calculate the slope of the line with data from the current and last cycles.
    cycleDerivative = (error - lastError) / deltaTime;

    //Save time data for next iteration.
    lastTime = currentTime;
  }
  //If we have no way to retrieve system time, estimate calculations.
  else
  {
    integralCumulation += error;
    cycleDerivative = (error - lastError);
  }

  //Prevent the integral cumulation from becoming overwhelmingly huge.
  if(integralCumulation > maxCumulation) integralCumulation = maxCumulation;
  if(integralCumulation < -maxCumulation) integralCumulation = -maxCumulation;

  //Calculate the system output based on data and PID gains.
  output = (int) ((error * _p) + (integralCumulation * _i) + (cycleDerivative * _d));

  //Save a record of this iteration's data.
  lastFeedback = currentFeedback;
  lastError = error;

  //Trim the output to the bounds if needed.
  if(outputBounded)
  {
    if(output > outputUpperBound) output = outputUpperBound;
    if(output < outputLowerBound) output = outputLowerBound;
  }

  _pidOutput(output);
}

/**
 * Sets the target of this PIDController.  This system will generate
 * correction outputs indended to guide the feedback variable (such
 * as position, velocity, etc.) toward the established target.
 */
void PIDController::setTarget(int t)
{
  target = t;
}

/**
 * Returns the current target of this PIDController.
 * @return The current target of this PIDController.
 */
int PIDController::getTarget()
{
  return target;
}

/**
 * Returns the latest output generated by this PIDController.  This value is
 * also delivered to the parent systems via the PIDOutput function pointer
 * provided in the constructor of this PIDController.
 * @return The latest output generated by this PIDController.
 */
int PIDController::getOutput()
{
  return output;
}

/**
 * Returns the last read feedback of this PIDController.
 */
int PIDController::getFeedback()
{
  return currentFeedback;
}

/**
 * Returns the value that the Proportional component is contributing to the output.
 * @return The value that the Proportional component is contributing to the output.
 */
int PIDController::getProportionalComponent()
{
  return (int) (error * _p);
}

/**
 * Returns the value that the Integral component is contributing to the output.
 * @return The value that the Integral component is contributing to the output.
 */
int PIDController::getIntegralComponent()
{
  return (int) (integralCumulation * _i);
}

/**
 * Returns the value that the Derivative component is contributing to the output.
 * @return The value that the Derivative component is contributing to the output.
 */
int PIDController::getDerivativeComponent()
{
  return (int) (cycleDerivative * _d);
}

/**
 * Sets the maximum value that the integral cumulation can reach.
 * @param max The maximum value of the integral cumulation.
 */
void PIDController::setMaxIntegralCumulation(int max)
{
  //If the new max value is less than 0, invert to make positive.
  if(max < 0)
  {
    max = -max;
  }

  //If the new max is not more than 1 then the cumulation is useless.
  if(max > 1)
  {
    maxCumulation = max;
  }
}

/**
 * Returns the maximum value that the integral value can cumulate to.
 * @return The maximum value that the integral value can cumulate to.
 */
int PIDController::getMaxIntegralCumulation()
{
  return maxCumulation;
}

/**
 * Returns the current cumulative integral value in this PIDController.
 * @return The current cumulative integral value in this PIDController.
 */
int PIDController::getIntegralCumulation()
{
  return integralCumulation;
}

/**
 * Enables or disables bounds on the input.  Bounds limit the upper and
 * lower values that this PIDController will ever accept as input.
 * Outlying values will be trimmed to the upper or lower bound as necessary.
 * @param bounded True to enable input bounds, False to disable.
 */
void PIDController::setInputBounded(bool bounded)
{
  inputBounded = bounded;
}

/**
 * Enables or disables bounds on the output.  Bounds limit the upper and lower
 * values that this PIDController will ever generate as output.
 * @param bounded True to enable output bounds, False to disable.
 */
void PIDController::setOutputBounded(bool bounded)
{
  outputBounded = bounded;
}

/**
 * Returns whether the input of this PIDController is being bounded.
 * @return True if the input of this PIDController is being bounded.
 */
bool PIDController::isInputBounded()
{
  return inputBounded;
}

/**
 * Returns whether the output of this PIDController is being bounded.
 * @return True if the output of this PIDController is being bounded.
 */
bool PIDController::isOutputBounded()
{
  return outputBounded;
}

/**
 * Sets bounds which limit the lower and upper extremes that this PIDController
 * accepts as inputs.  Outliers are trimmed to the lower and upper bounds.
 * Setting input bounds automatically enables input bounds.
 * @param lower The lower input bound.
 * @param upper The upper input bound.
 */
void PIDController::setInputBounds(int lower, int upper)
{
  if(upper > lower)
  {
    inputBounded = true;
    inputUpperBound = upper;
    inputLowerBound = lower;
  }
}

/**
 * Sets bounds which limit the lower and upper extremes that this PIDController
 * will ever generate as output.  Setting output bounds automatically enables
 * output bounds.
 * @param lower The lower output bound.
 * @param upper The upper output bound.
 */
void PIDController::setOutputBounds(int lower, int upper)
{
  if(upper > lower)
  {
    outputBounded = true;
    outputLowerBound = lower;
    outputUpperBound = upper;
  }
}

/**
 * Returns the lower input bound of this PIDController.
 * @return The lower input bound of this PIDController.
 */
int PIDController::getInputLowerBound()
{
  return inputLowerBound;
}

/**
 * Returns the upper input bound of this PIDController.
 * @return The upper input bound of this PIDController.
 */
int PIDController::getInputUpperBound()
{
  return inputUpperBound;
}

/**
 * Returns the lower output bound of this PIDController.
 * @return The lower output bound of this PIDController.
 */
int PIDController::getOutputLowerBound()
{
  return outputLowerBound;
}

/**
 * Returns the upper output bound of this PIDController.
 * @return The upper output bound of this PIDController.
 */
int PIDController::getOutputUpperBound()
{
  return outputUpperBound;
}

/**
 * Sets new values for all PID Gains.
 * @param p The new proportional gain.
 * @param i The new integral gain.
 * @param d The new derivative gain.
 */
void PIDController::setPID(double p, double i, double d)
{
  _p = p;
  _i = i;
  _d = d;
}

/**
 * Sets a new value for the proportional gain.
 * @param p The new proportional gain.
 */
void PIDController::setP(double p)
{
  _p = p;
}

/**
 * Sets a new value for the integral gain.
 * @param i The new integral gain.
 */
void PIDController::setI(double i)
{
  _i = i;
}

/**
 * Sets a new value for the derivative gain.
 * @param d The new derivative gain.
 */
void PIDController::setD(double d)
{
  _d = d;
}

/**
 * Returns the proportional gain.
 * @return The proportional gain.
 */
double PIDController::getP()
{
  return _p;
}

/**
 * Returns the integral gain.
 * @return The integral gain.
 */
double PIDController::getI()
{
  return _i;
}

/**
 * Returns the derivative gain.
 * @return The derivative gain.
 */
double PIDController::getD()
{
  return _d;
}

/**
 * Sets the function pointer to the PID Source.  A PID Source
 * is a function which returns a value to be used as the PIDController's
 * control feedback.  This value can be a reading from a sensor or other
 * data source that contains information regarding the system's actual
 * state.
 * Below is an example of using a PIDSource:
 *
 *    int pidSource()
 *    {
 *        return mySensor.getValue();
 *    }
 *    myPIDController.setPIDSource(pidSource);
 *
 * @param (*getFeedback) A function pointer that retrieves system feedback.
 */
void PIDController::setPIDSource(int (*pidSource)())
{
  _pidSource = pidSource;
}

/**
 * Sets the function pointer to the PID Output.  A PID Output
 * is a function which delivers a value to the parent system in order to guide
 * the system based on the PID loop's result.  This value can be delivered
 * directly to motors, to a variable that directs steering, or other means of
 * influencing the system.
 * Below is an example of using a PIDOutput:
 *
 *    void pidOutput(int output)
 *    {
 *        myMotor.write(output);
 *    }
 *    myPIDController.setPIDOutput(pidOutput);
 *
 * @param (*onUpdate) A function pointer that delivers system output.
 */
void PIDController::setPIDOutput(void (*pidOutput)(int output))
{
  _pidOutput = pidOutput;
}

/**
 * Use this to add a hook into the PID Controller that allows it to
 * read the system time no matter what platform this library is run
 * on.  Though developed for an arduino project, there is no code
 * tying this library to the arduino project.  To use the arduino
 * clock, however, register the time-getting function (millis())
 * like this:
 *
 *    myPIDController.registerTimeInput(millis);
 *    *Note that in this example, millis has no parentheses.
 *
 * @param (*getSystemTime) Pointer to a function that returns system time.
 */
void PIDController::registerTimeFunction(unsigned long (*getSystemTime)())
{
  _getSystemTime = getSystemTime;
  timeFunctionRegistered = true;
}
<|endoftext|>


<commit_before>#ifndef ANY_HPP
# define ANY_HPP
# pragma once

#include <cassert>

#include <cstdint>

#include <stdexcept>

#include <typeinfo>

#include <type_traits>

#include <utility>

namespace generic
{

class any
{
public:
  template <typename T>
  using remove_cvr = ::std::remove_cv<
    typename ::std::remove_reference<T>::type
  >;

  using typeid_t = ::std::uintptr_t;

  template <typename T>
  static constexpr char const c{};

  template <typename T>
  static constexpr typeid_t type_id() noexcept
  {
    return typeid_t(&c<T>);
  }

  any() = default;

  any(any const& other) :
    content(other.content ? other.content->cloner_(other.content) : nullptr)
  {
  }

  any(any&& other) noexcept { *this = ::std::move(other); }

  template<typename ValueType,
    typename = typename ::std::enable_if<
      !::std::is_same<
        any, typename ::std::decay<ValueType>::type
      >{}
    >::type
  >
  any(ValueType&& value) :
    content(new holder<typename remove_cvr<ValueType>::type>(
      ::std::forward<ValueType>(value)))
  {
  }

  ~any() { delete content; }

public: // modifiers
  void clear() { swap(any()); }

  bool empty() const noexcept { return !*this; }

  void swap(any& other) noexcept { ::std::swap(content, other.content); }

  void swap(any&& other) noexcept { ::std::swap(content, other.content); }

  any& operator=(any const& rhs)
  {
    return content == rhs.content ? *this : *this = any(rhs);
  }

  any& operator=(any&& rhs) noexcept { swap(rhs); return *this; }

  template<typename ValueType,
    typename = typename ::std::enable_if<
      !::std::is_same<
        any, typename ::std::decay<ValueType>::type
      >{}
    >::type
  >
  any& operator=(ValueType&& rhs)
  {
    return *this = any(::std::forward<ValueType>(rhs));
  }

public: // queries

  explicit operator bool() const noexcept { return content; }

  typeid_t type_id() const noexcept
  {
    return *this ? content->type_id_ : typeid_t(nullptr);
  }

  auto type() const noexcept -> decltype(type_id()) { return type_id(); }

public: // get

private: // types

  template <typename T>
  static constexpr T* begin(T& value) noexcept
  {
    return &value;
  }

  template <typename T, ::std::size_t N>
  static constexpr typename ::std::remove_all_extents<T>::type*
  begin(T (&array)[N]) noexcept
  {
    return begin(*array);
  }

  template <typename T>
  static constexpr T* end(T& value) noexcept
  {
    return &value + 1;
  }

  template <typename T, ::std::size_t N>
  static constexpr typename ::std::remove_all_extents<T>::type*
  end(T (&array)[N]) noexcept
  {
    return end(array[N - 1]);
  }

  struct placeholder
  {
    typeid_t const type_id_;

    placeholder* (* const cloner_)(placeholder*);

    virtual ~placeholder() = default;

  protected:

    placeholder(typeid_t const ti, decltype(cloner_) const c) noexcept :
      type_id_(ti),
      cloner_(c)
    {
    }
  };

  template <typename ValueType>
  struct holder : public placeholder
  {
  public: // constructor
    template <class T, typename U = ValueType>
    holder(T&& value,
      typename ::std::enable_if<
        !::std::is_array<U>{} &&
        !::std::is_copy_constructible<U>{}
      >::type* = nullptr) :
      placeholder(type_id<ValueType>(), throwing_cloner),
      held(::std::forward<T>(value))
    {
    }

    template <class T, typename U = ValueType>
    holder(T&& value,
      typename ::std::enable_if<
        !::std::is_array<U>{} &&
        ::std::is_copy_constructible<U>{}
      >::type* = nullptr) :
      placeholder(type_id<ValueType>(), cloner),
      held(::std::forward<T>(value))
    {
    }

    template <class T, typename U = ValueType>
    holder(T&& value,
      typename ::std::enable_if<
        ::std::is_array<U>{} &&
        ::std::is_move_assignable<
          typename ::std::remove_const<
            typename ::std::remove_all_extents<U>::type
          >::type
        >{} &&
        ::std::is_rvalue_reference<T&&>{}
      >::type* = nullptr) :
      placeholder(type_id<ValueType>(), throwing_cloner)
    {
      ::std::copy(::std::make_move_iterator(begin(value)),
        ::std::make_move_iterator(end(value)),
        begin(held));
    }

    template <class T, typename U = ValueType>
    holder(T&& value,
      typename ::std::enable_if<
        ::std::is_array<U>{} &&
        ::std::is_copy_assignable<
          typename ::std::remove_const<
            typename ::std::remove_all_extents<U>::type
          >::type
        >{} &&
        !::std::is_rvalue_reference<T&&>{}
      >::type* = nullptr) :
      placeholder(type_id<ValueType>(), cloner)
    {
      ::std::copy(begin(value), end(value), begin(held));
    }

    holder& operator=(holder const&) = delete;

    static placeholder* cloner(placeholder* const base)
    {
      return new holder<ValueType>(static_cast<holder*>(base)->held);
    }

    static placeholder* throwing_cloner(placeholder* const)
    {
      throw ::std::logic_error("");
    }

  public:
    typename ::std::remove_const<ValueType>::type held;
  };

private: // representation

  template<typename ValueType>
  friend ValueType* any_cast(any*) noexcept;

  template<typename ValueType>
  friend ValueType* unsafe_any_cast(any*) noexcept;

#ifdef NDEBUG
  template <typename U> friend U& get(any&) noexcept;
  template <typename U> friend U const& get(any const&) noexcept;
#else
  template <typename U> friend U& get(any&);
  template <typename U> friend U const& get(any const&);
#endif // NDEBUG

  placeholder* content{};
};

template<typename ValueType>
inline ValueType* unsafe_any_cast(any* const operand) noexcept
{
  return &static_cast<any::holder<ValueType>*>(operand->content)->held;
}

template<typename ValueType>
inline ValueType const* unsafe_any_cast(any const* const operand) noexcept
{
  return unsafe_any_cast<ValueType>(const_cast<any*>(operand));
}

template<typename ValueType>
inline ValueType* any_cast(any* const operand) noexcept
{
  return operand &&
    (operand->type_id() ==
      any::type_id<typename any::remove_cvr<ValueType>::type>()) ?
    &static_cast<any::holder<ValueType>*>(operand->content)->held :
    nullptr;
}

template<typename ValueType>
inline ValueType const* any_cast(any const* const operand) noexcept
{
  return any_cast<ValueType>(const_cast<any*>(operand));
}

template<typename ValueType>
inline ValueType any_cast(any& operand)
#ifdef NDEBUG
  noexcept
#endif
{
  using nonref = typename any::remove_cvr<ValueType>::type;

#ifndef NDEBUG
  auto const result(any_cast<nonref>(&operand));

  if (result)
  {
    return *result;
  }
  else
  {
    throw ::std::bad_cast();
  }
#else
  return *unsafe_any_cast<nonref>(&operand);
#endif // NDEBUG
}

template<typename ValueType>
inline ValueType any_cast(any const& operand) noexcept(
  noexcept(
    any_cast<typename any::remove_cvr<ValueType>::type>(
      const_cast<any&>(operand)
    )
  )
)
{
  using nonref = typename any::remove_cvr<ValueType>::type;

  return any_cast<nonref const&>(const_cast<any&>(operand));
}

template <typename U>
U& get(any& a)
#ifdef NDEBUG
  noexcept
#endif
{
  using nonref = typename ::generic::any::remove_cvr<U>::type;

#ifndef NDEBUG
  if (a.content && (a.type_id() == a.type_id<nonref>()))
  {
    return static_cast<any::holder<nonref>*>(a.content)->held;
  }
  else
  {
    throw ::std::bad_cast();
  }
#else
  return static_cast<any::holder<nonref>*>(a.content)->held;
#endif // NDEBUG
}

template <typename U>
U const& get(any const& a)
#ifdef NDEBUG
  noexcept
#endif
{
  using nonref = typename ::generic::any::remove_cvr<U>::type;

#ifndef NDEBUG
  if (a.content && (a.type_id() == a.type_id<nonref>()))
  {
    return static_cast<any::holder<nonref const>*>(a.content)->held;
  }
  else
  {
    throw ::std::bad_cast();
  }
#else
  return static_cast<any::holder<nonref>*>(a.content)->held;
#endif // NDEBUG
}

template <typename U>
typename ::std::enable_if<
  !(::std::is_enum<U>{} || ::std::is_fundamental<U>{}),
  U const&
>::type
cget(any const& a) noexcept(
  noexcept(get<U const&>(::std::declval<any const>())))
{
  return get<U const&>(a);
}

template <typename U>
typename ::std::enable_if<
  ::std::is_enum<U>{} || ::std::is_fundamental<U>{},
  U
>::type
cget(any const& a) noexcept(
  noexcept(get<U>(::std::declval<any const>())))
{
  return get<U>();
}

}

#endif // ANY_HPP
<commit_msg>some fixes<commit_after>#ifndef ANY_HPP
# define ANY_HPP
# pragma once

#include <cassert>

#include <cstdint>

#include <stdexcept>

#include <typeinfo>

#include <type_traits>

#include <utility>

namespace generic
{

class any
{
public:
  template <typename T>
  using remove_cvr = ::std::remove_cv<
    typename ::std::remove_reference<T>::type
  >;

  using typeid_t = ::std::uintptr_t;

  template <typename T>
  static typeid_t type_id() noexcept
  {
    static char c;

    return typeid_t(&c);
  }

  any() = default;

  any(any const& other) :
    content(other.content ? other.content->cloner_(other.content) : nullptr)
  {
  }

  any(any&& other) noexcept { *this = ::std::move(other); }

  template<typename ValueType,
    typename = typename ::std::enable_if<
      !::std::is_same<
        any, typename ::std::decay<ValueType>::type
      >{}
    >::type
  >
  any(ValueType&& value) :
    content(new holder<typename remove_cvr<ValueType>::type>(
      ::std::forward<ValueType>(value)))
  {
  }

  ~any() { delete content; }

public: // modifiers
  void clear() { swap(any()); }

  bool empty() const noexcept { return !*this; }

  void swap(any& other) noexcept { ::std::swap(content, other.content); }

  void swap(any&& other) noexcept { ::std::swap(content, other.content); }

  any& operator=(any const& rhs)
  {
    return content == rhs.content ? *this : *this = any(rhs);
  }

  any& operator=(any&& rhs) noexcept { swap(rhs); return *this; }

  template<typename ValueType,
    typename = typename ::std::enable_if<
      !::std::is_same<
        any, typename ::std::decay<ValueType>::type
      >{}
    >::type
  >
  any& operator=(ValueType&& rhs)
  {
    return *this = any(::std::forward<ValueType>(rhs));
  }

public: // queries

  explicit operator bool() const noexcept { return content; }

  typeid_t type_id() const noexcept
  {
    return *this ? content->type_id_ : typeid_t(nullptr);
  }

  auto type() const noexcept -> decltype(type_id()) { return type_id(); }

public: // get

private: // types

  template <typename T>
  static constexpr T* begin(T& value) noexcept
  {
    return &value;
  }

  template <typename T, ::std::size_t N>
  static constexpr typename ::std::remove_all_extents<T>::type*
  begin(T (&array)[N]) noexcept
  {
    return begin(*array);
  }

  template <typename T>
  static constexpr T* end(T& value) noexcept
  {
    return &value + 1;
  }

  template <typename T, ::std::size_t N>
  static constexpr typename ::std::remove_all_extents<T>::type*
  end(T (&array)[N]) noexcept
  {
    return end(array[N - 1]);
  }

  struct placeholder
  {
    typeid_t const type_id_;

    placeholder* (* const cloner_)(placeholder*);

    virtual ~placeholder() = default;

  protected:

    placeholder(typeid_t const ti, decltype(cloner_) const c) noexcept :
      type_id_(ti),
      cloner_(c)
    {
    }
  };

  template <typename ValueType>
  struct holder : public placeholder
  {
  public: // constructor
    template <class T, typename U = ValueType>
    holder(T&& value,
      typename ::std::enable_if<
        !::std::is_array<U>{} &&
        !::std::is_copy_constructible<U>{}
      >::type* = nullptr) :
      placeholder(type_id<ValueType>(), throwing_cloner),
      held(::std::forward<T>(value))
    {
    }

    template <class T, typename U = ValueType>
    holder(T&& value,
      typename ::std::enable_if<
        !::std::is_array<U>{} &&
        ::std::is_copy_constructible<U>{}
      >::type* = nullptr) :
      placeholder(type_id<ValueType>(), cloner),
      held(::std::forward<T>(value))
    {
    }

    template <class T, typename U = ValueType>
    holder(T&& value,
      typename ::std::enable_if<
        ::std::is_array<U>{} &&
        ::std::is_move_assignable<
          typename ::std::remove_const<
            typename ::std::remove_all_extents<U>::type
          >::type
        >{} &&
        ::std::is_rvalue_reference<T&&>{}
      >::type* = nullptr) :
      placeholder(type_id<ValueType>(), throwing_cloner)
    {
      ::std::copy(::std::make_move_iterator(begin(value)),
        ::std::make_move_iterator(end(value)),
        begin(held));
    }

    template <class T, typename U = ValueType>
    holder(T&& value,
      typename ::std::enable_if<
        ::std::is_array<U>{} &&
        ::std::is_copy_assignable<
          typename ::std::remove_const<
            typename ::std::remove_all_extents<U>::type
          >::type
        >{} &&
        !::std::is_rvalue_reference<T&&>{}
      >::type* = nullptr) :
      placeholder(type_id<ValueType>(), cloner)
    {
      ::std::copy(begin(value), end(value), begin(held));
    }

    holder& operator=(holder const&) = delete;

    static placeholder* cloner(placeholder* const base)
    {
      return new holder<ValueType>(static_cast<holder*>(base)->held);
    }

    static placeholder* throwing_cloner(placeholder* const)
    {
      throw ::std::logic_error("");
    }

  public:
    typename ::std::remove_const<ValueType>::type held;
  };

private: // representation

  template<typename ValueType>
  friend ValueType* any_cast(any*) noexcept;

  template<typename ValueType>
  friend ValueType* unsafe_any_cast(any*) noexcept;

#ifdef NDEBUG
  template <typename U> friend U& get(any&) noexcept;
  template <typename U> friend U const& get(any const&) noexcept;
#else
  template <typename U> friend U& get(any&);
  template <typename U> friend U const& get(any const&);
#endif // NDEBUG

  placeholder* content{};
};

template<typename ValueType>
inline ValueType* unsafe_any_cast(any* const operand) noexcept
{
  return &static_cast<any::holder<ValueType>*>(operand->content)->held;
}

template<typename ValueType>
inline ValueType const* unsafe_any_cast(any const* const operand) noexcept
{
  return unsafe_any_cast<ValueType>(const_cast<any*>(operand));
}

template<typename ValueType>
inline ValueType* any_cast(any* const operand) noexcept
{
  return operand &&
    (operand->type_id() ==
      any::type_id<typename any::remove_cvr<ValueType>::type>()) ?
    &static_cast<any::holder<ValueType>*>(operand->content)->held :
    nullptr;
}

template<typename ValueType>
inline ValueType const* any_cast(any const* const operand) noexcept
{
  return any_cast<ValueType>(const_cast<any*>(operand));
}

template<typename ValueType>
inline ValueType any_cast(any& operand)
#ifdef NDEBUG
  noexcept
#endif
{
  using nonref = typename any::remove_cvr<ValueType>::type;

#ifndef NDEBUG
  auto const result(any_cast<nonref>(&operand));

  if (result)
  {
    return *result;
  }
  else
  {
    throw ::std::bad_cast();
  }
#else
  return *unsafe_any_cast<nonref>(&operand);
#endif // NDEBUG
}

template<typename ValueType>
inline ValueType any_cast(any const& operand) noexcept(
  noexcept(
    any_cast<typename any::remove_cvr<ValueType>::type>(
      const_cast<any&>(operand)
    )
  )
)
{
  using nonref = typename any::remove_cvr<ValueType>::type;

  return any_cast<nonref const&>(const_cast<any&>(operand));
}

template <typename U>
U& get(any& a)
#ifdef NDEBUG
  noexcept
#endif
{
  using nonref = typename ::generic::any::remove_cvr<U>::type;

#ifndef NDEBUG
  if (a.content && (a.type_id() == a.type_id<nonref>()))
  {
    return static_cast<any::holder<nonref>*>(a.content)->held;
  }
  else
  {
    throw ::std::bad_cast();
  }
#else
  return static_cast<any::holder<nonref>*>(a.content)->held;
#endif // NDEBUG
}

template <typename U>
U const& get(any const& a)
#ifdef NDEBUG
  noexcept
#endif
{
  using nonref = typename ::generic::any::remove_cvr<U>::type;

#ifndef NDEBUG
  if (a.content && (a.type_id() == a.type_id<nonref>()))
  {
    return static_cast<any::holder<nonref const>*>(a.content)->held;
  }
  else
  {
    throw ::std::bad_cast();
  }
#else
  return static_cast<any::holder<nonref>*>(a.content)->held;
#endif // NDEBUG
}

template <typename U>
typename ::std::enable_if<
  !(::std::is_enum<U>{} || ::std::is_fundamental<U>{}),
  U const&
>::type
cget(any const& a) noexcept(
  noexcept(get<U const&>(::std::declval<any const>())))
{
  return get<U const&>(a);
}

template <typename U>
typename ::std::enable_if<
  ::std::is_enum<U>{} || ::std::is_fundamental<U>{},
  U
>::type
cget(any const& a) noexcept(
  noexcept(get<U>(::std::declval<any const>())))
{
  return get<U>();
}

}

#endif // ANY_HPP
<|endoftext|>


<commit_before>#include "blp.h"
#include <string.h>

// Forward declaration of "internal" functions
tBGRAPixel* blp_convert_paletted_no_alpha(uint8_t* pSrc, tBLP2Header* pHeader, unsigned int width, unsigned int height);
tBGRAPixel* blp_convert_paletted_alpha8(uint8_t* pSrc, tBLP2Header* pHeader, unsigned int width, unsigned int height);

tBLP2Header* blp_processFile(FILE* pFile)
{
    tBLP2Header* pHeader = new tBLP2Header();

    fseek(pFile, 0, SEEK_SET);
    fread((void*) pHeader, sizeof(uint8_t), 4, pFile);

    if (strncmp((char*) pHeader->magic, "BLP2", 4) != 0)
    {
        delete pHeader;
        return 0;
    }

    fread((void*) &pHeader->type, sizeof(tBLP2Header) - 4 * sizeof(uint8_t), 1, pFile);

    pHeader->nbMipLevels = 0;
    while ((pHeader->offsets[pHeader->nbMipLevels] != 0) && (pHeader->nbMipLevels < 16))
        ++pHeader->nbMipLevels;

    return pHeader;
}

tBLPFormat blp_format(tBLP2Header* pHeader)
{
    if (pHeader->encoding == BLP_ENCODING_UNCOMPRESSED)
        return tBLPFormat((pHeader->encoding << 16) | (pHeader->alphaDepth << 8));

    return tBLPFormat((pHeader->encoding << 16) | (pHeader->alphaDepth << 8) | pHeader->alphaEncoding);
}

unsigned int blp_width(tBLP2Header* pHeader, unsigned int mipLevel)
{
    // Check the mip level
    if (mipLevel >= pHeader->nbMipLevels)
        mipLevel = pHeader->nbMipLevels - 1;

    return (pHeader->width >> mipLevel);
}

unsigned int blp_height(tBLP2Header* pHeader, unsigned int mipLevel)
{
    // Check the mip level
    if (mipLevel >= pHeader->nbMipLevels)
        mipLevel = pHeader->nbMipLevels - 1;

    return (pHeader->height >> mipLevel);
}

tBGRAPixel* blp_convert(FILE* pFile, tBLP2Header* pHeader, unsigned int mipLevel)
{
    // Check the mip level
    if (mipLevel >= pHeader->nbMipLevels)
        mipLevel = pHeader->nbMipLevels - 1;

    // Declarations
    unsigned int width = blp_width(pHeader, mipLevel);
    unsigned int height = blp_height(pHeader, mipLevel);
    uint8_t* pSrc = new uint8_t[pHeader->lengths[mipLevel]];
    tBGRAPixel* pDst = 0;

    // Read the data from the file
    fseek(pFile, pHeader->offsets[mipLevel], SEEK_SET);
    fread((void*) pSrc, sizeof(uint8_t), pHeader->lengths[mipLevel], pFile);

    switch (blp_format(pHeader))
    {
        case BLP_FORMAT_PALETTED_NO_ALPHA: pDst = blp_convert_paletted_no_alpha(pSrc, pHeader, width, height); break;
        case BLP_FORMAT_PALETTED_ALPHA_8:  pDst = blp_convert_paletted_alpha8(pSrc, pHeader, width, height); break;
        default:                           break;
    }

    delete[] pSrc;

    return pDst;
}

std::string blp_asString(tBLPFormat format)
{
    switch (format)
    {
        case BLP_FORMAT_PALETTED_NO_ALPHA: return "Uncompressed paletted image, no alpha";
        case BLP_FORMAT_PALETTED_ALPHA_1:  return "Uncompressed paletted image, 1-bit alpha";
        case BLP_FORMAT_PALETTED_ALPHA_8:  return "Uncompressed paletted image, 8-bit alpha";
        case BLP_FORMAT_DXT1_NO_ALPHA:     return "DXT1, no alpha";
        case BLP_FORMAT_DXT1_ALPHA_1:      return "DXT1, 1-bit alpha";
        case BLP_FORMAT_DXT3_ALPHA_4:      return "DXT3, 4-bit alpha";
        case BLP_FORMAT_DXT3_ALPHA_8:      return "DXT3, 8-bit alpha";
        case BLP_FORMAT_DXT5_ALPHA_8:      return "DXT5, 8-bit alpha";
        default:                           return "Unknown";
    }
}

tBGRAPixel* blp_convert_paletted_no_alpha(uint8_t* pSrc, tBLP2Header* pHeader, unsigned int width, unsigned int height)
{
    tBGRAPixel* pBuffer = new tBGRAPixel[width * height];
    tBGRAPixel* pDst = pBuffer;

    for (unsigned int y = 0; y < height; ++y)
    {
        for (unsigned int x = 0; x < width; ++x)
        {
            *pDst = pHeader->palette[*pSrc];
            pDst->a = 0xFF;

            ++pSrc;
            ++pDst;
        }
    }

    return pBuffer;
}

tBGRAPixel* blp_convert_paletted_alpha8(uint8_t* pSrc, tBLP2Header* pHeader, unsigned int width, unsigned int height)
{
    tBGRAPixel* pBuffer = new tBGRAPixel[width * height];
    tBGRAPixel* pDst = pBuffer;

    uint8_t* pIndices = pSrc;
    uint8_t* pAlpha = pSrc + width * height;

    for (unsigned int y = 0; y < height; ++y)
    {
        for (unsigned int x = 0; x < width; ++x)
        {
            *pDst = pHeader->palette[*pIndices];
            pDst->a = *pAlpha;

            ++pIndices;
            ++pAlpha;
            ++pDst;
        }
    }

    return pBuffer;
}
<commit_msg>Implement conversion of paletted images with 1-bit alpha<commit_after>#include "blp.h"
#include <string.h>

// Forward declaration of "internal" functions
tBGRAPixel* blp_convert_paletted_no_alpha(uint8_t* pSrc, tBLP2Header* pHeader, unsigned int width, unsigned int height);
tBGRAPixel* blp_convert_paletted_alpha1(uint8_t* pSrc, tBLP2Header* pHeader, unsigned int width, unsigned int height);
tBGRAPixel* blp_convert_paletted_alpha8(uint8_t* pSrc, tBLP2Header* pHeader, unsigned int width, unsigned int height);

tBLP2Header* blp_processFile(FILE* pFile)
{
    tBLP2Header* pHeader = new tBLP2Header();

    fseek(pFile, 0, SEEK_SET);
    fread((void*) pHeader, sizeof(uint8_t), 4, pFile);

    if (strncmp((char*) pHeader->magic, "BLP2", 4) != 0)
    {
        delete pHeader;
        return 0;
    }

    fread((void*) &pHeader->type, sizeof(tBLP2Header) - 4 * sizeof(uint8_t), 1, pFile);

    pHeader->nbMipLevels = 0;
    while ((pHeader->offsets[pHeader->nbMipLevels] != 0) && (pHeader->nbMipLevels < 16))
        ++pHeader->nbMipLevels;

    return pHeader;
}

tBLPFormat blp_format(tBLP2Header* pHeader)
{
    if (pHeader->encoding == BLP_ENCODING_UNCOMPRESSED)
        return tBLPFormat((pHeader->encoding << 16) | (pHeader->alphaDepth << 8));

    return tBLPFormat((pHeader->encoding << 16) | (pHeader->alphaDepth << 8) | pHeader->alphaEncoding);
}

unsigned int blp_width(tBLP2Header* pHeader, unsigned int mipLevel)
{
    // Check the mip level
    if (mipLevel >= pHeader->nbMipLevels)
        mipLevel = pHeader->nbMipLevels - 1;

    return (pHeader->width >> mipLevel);
}

unsigned int blp_height(tBLP2Header* pHeader, unsigned int mipLevel)
{
    // Check the mip level
    if (mipLevel >= pHeader->nbMipLevels)
        mipLevel = pHeader->nbMipLevels - 1;

    return (pHeader->height >> mipLevel);
}

tBGRAPixel* blp_convert(FILE* pFile, tBLP2Header* pHeader, unsigned int mipLevel)
{
    // Check the mip level
    if (mipLevel >= pHeader->nbMipLevels)
        mipLevel = pHeader->nbMipLevels - 1;

    // Declarations
    unsigned int width = blp_width(pHeader, mipLevel);
    unsigned int height = blp_height(pHeader, mipLevel);
    uint8_t* pSrc = new uint8_t[pHeader->lengths[mipLevel]];
    tBGRAPixel* pDst = 0;

    // Read the data from the file
    fseek(pFile, pHeader->offsets[mipLevel], SEEK_SET);
    fread((void*) pSrc, sizeof(uint8_t), pHeader->lengths[mipLevel], pFile);

    switch (blp_format(pHeader))
    {
        case BLP_FORMAT_PALETTED_NO_ALPHA: pDst = blp_convert_paletted_no_alpha(pSrc, pHeader, width, height); break;
        case BLP_FORMAT_PALETTED_ALPHA_1:  pDst = blp_convert_paletted_alpha1(pSrc, pHeader, width, height); break;
        case BLP_FORMAT_PALETTED_ALPHA_8:  pDst = blp_convert_paletted_alpha8(pSrc, pHeader, width, height); break;
        default:                           break;
    }

    delete[] pSrc;

    return pDst;
}

std::string blp_asString(tBLPFormat format)
{
    switch (format)
    {
        case BLP_FORMAT_PALETTED_NO_ALPHA: return "Uncompressed paletted image, no alpha";
        case BLP_FORMAT_PALETTED_ALPHA_1:  return "Uncompressed paletted image, 1-bit alpha";
        case BLP_FORMAT_PALETTED_ALPHA_8:  return "Uncompressed paletted image, 8-bit alpha";
        case BLP_FORMAT_DXT1_NO_ALPHA:     return "DXT1, no alpha";
        case BLP_FORMAT_DXT1_ALPHA_1:      return "DXT1, 1-bit alpha";
        case BLP_FORMAT_DXT3_ALPHA_4:      return "DXT3, 4-bit alpha";
        case BLP_FORMAT_DXT3_ALPHA_8:      return "DXT3, 8-bit alpha";
        case BLP_FORMAT_DXT5_ALPHA_8:      return "DXT5, 8-bit alpha";
        default:                           return "Unknown";
    }
}

tBGRAPixel* blp_convert_paletted_no_alpha(uint8_t* pSrc, tBLP2Header* pHeader, unsigned int width, unsigned int height)
{
    tBGRAPixel* pBuffer = new tBGRAPixel[width * height];
    tBGRAPixel* pDst = pBuffer;

    for (unsigned int y = 0; y < height; ++y)
    {
        for (unsigned int x = 0; x < width; ++x)
        {
            *pDst = pHeader->palette[*pSrc];
            pDst->a = 0xFF;

            ++pSrc;
            ++pDst;
        }
    }

    return pBuffer;
}

tBGRAPixel* blp_convert_paletted_alpha8(uint8_t* pSrc, tBLP2Header* pHeader, unsigned int width, unsigned int height)
{
    tBGRAPixel* pBuffer = new tBGRAPixel[width * height];
    tBGRAPixel* pDst = pBuffer;

    uint8_t* pIndices = pSrc;
    uint8_t* pAlpha = pSrc + width * height;

    for (unsigned int y = 0; y < height; ++y)
    {
        for (unsigned int x = 0; x < width; ++x)
        {
            *pDst = pHeader->palette[*pIndices];
            pDst->a = *pAlpha;

            ++pIndices;
            ++pAlpha;
            ++pDst;
        }
    }

    return pBuffer;
}

tBGRAPixel* blp_convert_paletted_alpha1(uint8_t* pSrc, tBLP2Header* pHeader, unsigned int width, unsigned int height)
{
    tBGRAPixel* pBuffer = new tBGRAPixel[width * height];
    tBGRAPixel* pDst = pBuffer;

    uint8_t* pIndices = pSrc;
    uint8_t* pAlpha = pSrc + width * height;
    uint8_t counter = 0;

    for (unsigned int y = 0; y < height; ++y)
    {
        for (unsigned int x = 0; x < width; ++x)
        {
            *pDst = pHeader->palette[*pIndices];
            pDst->a = (*pAlpha & (1 << counter) ? 0xFF : 0x00);

            ++pIndices;
            ++pDst;

            ++counter;
            if (counter == 8)
            {
                ++pAlpha;
                counter = 0;
            }
        }
    }

    return pBuffer;
}
<|endoftext|>


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

namespace rcbg {

template<typename U>
inline bool get_bit(U val, unsigned pos) {
	return val & ((U)1 << pos);
}
template<typename U>
inline U set_bit(U val, unsigned pos, bool to) {
	return (val & ~((U)1 << pos)) | ((U)to << pos);
}

//the random cycle bit generator - random number generator
template<typename T,
		 unsigned int bit_pos_left = ((sizeof(T) * 8) / 3) + 1,
		 unsigned int bit_pos_start = bit_pos_left * 2>
class rcb_generator {
private:
	T val;
	T last;
	T cnt = 1;
	//only first two flags used (1 << 1) is "left" (1 << 0) is start
	char flags = 0;
	inline static T shift_transform(T val, int i, bool& start_bit) {
        val ^= (T)start_bit << i;
		start_bit ^= get_bit(val, i);
		return val;
	}
	static T generate(T val, bool left, bool start_bit) {
		if(left)
			for(int i = 0; i < (int)(sizeof(T) * 8); ++i)
				val = shift_transform(val, i, start_bit);
		else
			for(int i = (sizeof(T) * 8) - 1; i > -1; --i)
				val = shift_transform(val, i, start_bit);
		return val;
	}
	T generate() {
		//set the flags left and start bit
		bool left = (get_bit(val, bit_pos_left) ^ get_bit(flags, 1));
		bool start_bit = (get_bit(val, bit_pos_start) ^ get_bit(flags, 0));
		flags = set_bit(flags, 1, left);
		flags = set_bit(flags, 0, start_bit);

		T tmpVal = val;
		T tfrm = generate(val, left, start_bit);

		val = tfrm;
		last = tfrm ^ tmpVal ^ last;

		return last;
	}
public:
	rcb_generator(T rnd) : val(rnd), last(~rnd) {}
	inline T rand() {
		T tmp_cnt = cnt++;
		if(cnt == 0) ++cnt;
		if(tmp_cnt % 2 != 0) --tmp_cnt;
		return val = (generate() ^ ((generate() << 1) * ((generate() << 1) * tmp_cnt)));
	}
};

}
<commit_msg>random offset on first value<commit_after>/*----------------------------------------------------------------------------------*\
 |																					|
 | rcb_generator.hpp 																|
 |																					|
 | Copyright (c) 2016 Richard Cookman												|
 |																					|
 | Permission is hereby granted, free of charge, to any person obtaining a copy		|
 | of this software and associated documentation files (the "Software"), to deal	|
 | in the Software without restriction, including without limitation the rights		|
 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell		|
 | copies of the Software, and to permit persons to whom the Software is			|
 | furnished to do so, subject to the following conditions:							|
 |																					|
 | The above copyright notice and this permission notice shall be included in all	|
 | copies or substantial portions of the Software.									|
 |																					|
 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR		|
 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,			|
 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE		|
 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER			|
 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,	|
 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE	|
 | SOFTWARE.																		|
 |																					|
\*----------------------------------------------------------------------------------*/
#pragma once

namespace rcbg {

template<typename U>
inline bool get_bit(U val, unsigned pos) {
	return val & ((U)1 << pos);
}
template<typename U>
inline U set_bit(U val, unsigned pos, bool to) {
	return (val & ~((U)1 << pos)) | ((U)to << pos);
}

//the random cycle bit generator - random number generator
template<typename T,
		 unsigned int bit_pos_left = ((sizeof(T) * 8) / 3) + 1,
		 unsigned int bit_pos_start = bit_pos_left * 2>
class rcb_generator {
private:
	T val;
	T last;
	T cnt = 1;
	//only first two flags used (1 << 1) is "left" (1 << 0) is start
	char flags = 0;
	inline static T shift_transform(T val, int i, bool& start_bit) {
        val ^= (T)start_bit << i;
		start_bit ^= get_bit(val, i);
		return val;
	}
	static T generate(T val, bool left, bool start_bit) {
		if(left)
			for(int i = 0; i < (int)(sizeof(T) * 8); ++i)
				val = shift_transform(val, i, start_bit);
		else
			for(int i = (sizeof(T) * 8) - 1; i > -1; --i)
				val = shift_transform(val, i, start_bit);
		return val;
	}
	T generate() {
		//set the flags left and start bit
		bool left = (get_bit(val, bit_pos_left) ^ get_bit(flags, 1));
		bool start_bit = (get_bit(val, bit_pos_start) ^ get_bit(flags, 0));
		flags = set_bit(flags, 1, left);
		flags = set_bit(flags, 0, start_bit);

		T tmpVal = val;
		T tfrm = generate(val, left, start_bit);

		val = tfrm;
		last = tfrm ^ tmpVal ^ last;

		return last;
	}
public:
	rcb_generator(T rnd) : val(rnd + 10), last(~(rnd - 10)) {}
	inline T rand() {
		T tmp_cnt = cnt++;
		if(cnt == 0) ++cnt;
		if(tmp_cnt % 2 != 0) --tmp_cnt;
		return val = (generate() ^ ((generate() << 1) * ((generate() << 1) * tmp_cnt)));
	}
};

}
<|endoftext|>


<commit_before>/* Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.html */
#ifndef __RAPICORN_SVG_HH__
#define __RAPICORN_SVG_HH__

#include <rcore/objects.hh>
#include <cairo.h>
#include <math.h>

namespace Rapicorn {
namespace Svg {

class Element;
class File;

/// Information struct for SVG element size units.
struct Info {
  String id; double em, ex;
  Info () : em (0), ex (0) {}
};

/// A BBox provides bounding box information for SVG elements.
struct BBox {
  double x, y, width, height;
  BBox ();
  BBox (double, double, double, double);
};

enum class RenderSize { STATIC, ZOOM, STRETCH, WARP };

typedef std::shared_ptr<Element> ElementP;      ///< Smart pointer to an Svg::Element.
/// An SVG Element can be queried for size, rendered to pixmaps and scaled in various ways.
class Element {
public:
  virtual Info  info            () = 0;                 ///< Provides information on size units.
  virtual BBox  bbox            () = 0;                 ///< Provides the bounding box of an element.
  virtual BBox  enfolding_bbox  (BBox &inner) = 0;      ///< Provides the size of an element for a given containee size.
  virtual BBox  containee_bbox  () = 0;                 ///< Provides the bounding box of a containee if supported.
  virtual BBox  containee_bbox  (BBox &_resized) = 0;   ///< Provides the containee size for a given element size. FIXME: scaling
  virtual bool  render          (cairo_surface_t *surface, RenderSize rsize = RenderSize::ZOOM, double xscale = 1,
                                 double yscale = 1) = 0;///< Renders a scaled SVG element into a cairo_surface_t.
  static const ElementP none    ();                     ///< Returns null ElementP, which yields false in boolean tests.
protected: // Impl details
  ~Element() {}                         ///< Internal destructor, Svg::ElementP automatically manages the Element class lifetime.
};

typedef std::shared_ptr<File> FileP;            ///< Smart pointer to an Svg::File.
/// The File class represents a successfully loaded SVG file.
class File {
public:
  virtual void          dump_tree       () = 0;
  virtual ElementP      lookup          (const String &elementid) = 0;  ///< Lookup an SVG element from an SVG File.
  static  void          add_search_dir  (const String &absdir);         ///< Adds a directory for relative Svg::File::load() calls.
  static  FileP         load            (const String &svgfilename);    ///< Load an SVG file, returns non-null on success and sets errno.
protected: // Impl details
  ~File() {}                            ///< Internal destructor, Svg::FileP automatically manages the File class lifetime.
};

} // Svg
} // Rapicorn

#endif /* __RAPICORN_SVG_HH__ */
<commit_msg>RSVG: document sizing modes<commit_after>/* Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.html */
#ifndef __RAPICORN_SVG_HH__
#define __RAPICORN_SVG_HH__

#include <rcore/objects.hh>
#include <cairo.h>
#include <math.h>

namespace Rapicorn {
namespace Svg {

class Element;
class File;

/// Information struct for SVG element size units.
struct Info {
  String id; double em, ex;
  Info () : em (0), ex (0) {}
};

/// A BBox provides bounding box information for SVG elements.
struct BBox {
  double x, y, width, height;
  BBox ();
  BBox (double, double, double, double);
};

enum class RenderSize {
  STATIC,       ///< Keep the SVG elements native size.
  ZOOM,         ///< Zoom the SVG element according to scale factors.
  STRETCH,      ///< Adjust the SVG element according to scale factors while keeping thickness, roundings, etc.
  WARP          ///< Stretch only inner parts of the SVG element according to scale factors.
};

typedef std::shared_ptr<Element> ElementP;      ///< Smart pointer to an Svg::Element.
/// An SVG Element can be queried for size, rendered to pixmaps and scaled in various ways.
class Element {
public:
  virtual Info  info            () = 0;                 ///< Provides information on size units.
  virtual BBox  bbox            () = 0;                 ///< Provides the bounding box of an element.
  virtual BBox  enfolding_bbox  (BBox &inner) = 0;      ///< Provides the size of an element for a given containee size.
  virtual BBox  containee_bbox  () = 0;                 ///< Provides the bounding box of a containee if supported.
  virtual BBox  containee_bbox  (BBox &_resized) = 0;   ///< Provides the containee size for a given element size. FIXME: scaling
  virtual bool  render          (cairo_surface_t *surface, RenderSize rsize = RenderSize::ZOOM, double xscale = 1,
                                 double yscale = 1) = 0;///< Renders a scaled SVG element into a cairo_surface_t.
  static const ElementP none    ();                     ///< Returns null ElementP, which yields false in boolean tests.
protected: // Impl details
  ~Element() {}                         ///< Internal destructor, Svg::ElementP automatically manages the Element class lifetime.
};

typedef std::shared_ptr<File> FileP;            ///< Smart pointer to an Svg::File.
/// The File class represents a successfully loaded SVG file.
class File {
public:
  virtual void          dump_tree       () = 0;
  virtual ElementP      lookup          (const String &elementid) = 0;  ///< Lookup an SVG element from an SVG File.
  static  void          add_search_dir  (const String &absdir);         ///< Adds a directory for relative Svg::File::load() calls.
  static  FileP         load            (const String &svgfilename);    ///< Load an SVG file, returns non-null on success and sets errno.
protected: // Impl details
  ~File() {}                            ///< Internal destructor, Svg::FileP automatically manages the File class lifetime.
};

} // Svg
} // Rapicorn

#endif /* __RAPICORN_SVG_HH__ */
<|endoftext|>