<commit_before>#include "HeaterMeterClient.h"
#if defined(ESP8266)
  #include <ESP8266HTTPClient.h>
#else
  #include <HTTPClient.h>
#endif

#define CLIENT_HTTP_TIMEOUT 6000

//static void hexdump(char* data, size_t len)
//{
//  while (len > 0)
//  {
//    uint8_t linepos = 0;
//    char* linestart = data;
//    // Binary part
//    while (len > 0 && linepos < 16)
//    {
//      if (*data < 0x0f)
//        Serial.write('0');
//      Serial.print(*data, HEX);
//      Serial.write(' ');
//      ++data;
//      ++linepos;
//      --len;
//    }
//
//    // Spacer to align last line
//    for (uint8_t i = linepos; i < 16; ++i)
//      Serial.print("   ");
//
//    // ASCII part
//    for (uint8_t i = 0; i < linepos; ++i)
//      Serial.write((linestart[i] < ' ') ? '.' : linestart[i]);
//    Serial.write('\n');
//  }
//}

void HeaterMeterClientProbe::clear(void)
{
  Name[0] = '\0';
  //float Temperature;
  HasTemperature = false;
  AlarmLow = 0;;
  AlarmHigh = 0;
  AlarmRinging = ' ';
  //float TemperatureDph;
  HasTemperatureDph = false;
}

void HeaterMeterClientPidInternals::clear(void)
{
  LastUpdate = 0;

  P = 0.0f;
  I = 0.0f;
  D = 0.0f;
  dT = 0.0f;
}

void HeaterMeterClientPidOutput::clear(void)
{
  Enabled = false;
  Current = 0;
  Fan = 0;
  Servo = 0;

  Internals.clear();
}

void HeaterMeterClientPid::clear(void)
{
  UpdateUtc = 0;
  for (uint8_t i = 0; i < TEMP_COUNT; ++i)
    Probes[i].clear();
  Output.clear();
}

HeaterMeterClient::HeaterMeterClient(const char* host) :
  _protocolState(hpsNone), _reconnectDelay(60000)
{
  strlcpy(_host, host, sizeof(_host));
}

void HeaterMeterClient::asynctcp_onData(void* arg, AsyncClient* c, void* data, size_t len)
{
  ((HeaterMeterClient*)arg)->onData_cb(data, len);
}

void HeaterMeterClient::asynctcp_onConnect(void* arg, AsyncClient* c)
{
  ((HeaterMeterClient*)arg)->onConnect_cb();
}

void HeaterMeterClient::asynctcp_onDisconnect(void* arg, AsyncClient* c)
{
  ((HeaterMeterClient*)arg)->onDisconnect_cb();
}

void HeaterMeterClient::asynctcp_onError(void* arg, AsyncClient* c, err_t err)
{
  ((HeaterMeterClient*)arg)->onError_cb(err);
}

bool HeaterMeterClient::readLine(char** pos, size_t* len)
{
  // Looking at HTTP lines each ending in [0x0d] 0x0a
  bool wasNl = false;
  while ((*len > 0) && (_lineBufferPos < sizeof(_lineBuffer) - 1) & !wasNl)
  {
    _lineBuffer[_lineBufferPos++] = **pos;
    wasNl = (**pos == 0x0a);
    ++* pos;
    --* len;
  } /* while building line */

  _lineBuffer[_lineBufferPos] = '\0';
  return wasNl;
}

void HeaterMeterClient::updateProxyFromJson(JsonDocument& doc)
{
  for (uint8_t i = 0; i < TEMP_COUNT; ++i)
  {
    HeaterMeterClientProbe& p = state.Probes[i];
    const JsonObject& jp = doc["temps"][i];

    // Name
    strlcpy(p.Name, jp["n"], sizeof(p.Name));
    // Temperature
    const JsonVariant& t = jp["c"];
    p.HasTemperature = !t.isNull();
    if (p.HasTemperature)
      p.Temperature = t;
    else
      p.Temperature = 0.0f;
    // DPH
    p.HasTemperatureDph = jp.containsKey("dph");
    if (p.HasTemperatureDph)
      p.TemperatureDph = jp["dph"];
    else
      p.TemperatureDph = 0.0f;
    // Alarms
    p.AlarmHigh = jp["a"]["h"];
    p.AlarmLow = jp["a"]["l"];
    p.AlarmRinging = jp["a"]["r"].isNull() ? ' ' : jp["a"]["r"];
  }

  // Output
  const JsonObject& output = doc["fan"];
  state.Output.Current = output["c"];
  state.Output.Fan = output["f"];
  state.Output.Servo = output["s"];
  state.Output.Enabled = !doc["set"].isNull();

  // Setpoint
  if (state.Output.Enabled)
    state.Setpoint = doc["set"];
  else
    state.Setpoint = 0;

  // Lid
  state.LidCountdown = doc["lid"];

  // Main
  state.UpdateUtc = doc["time"];

  setNotifyPending(nptHmStatus);
}

void HeaterMeterClient::handleHmStatus(char* data)
{
  StaticJsonDocument<1024> doc;
  DeserializationError err = deserializeJson(doc, data);
  if (err)
  {
    Serial.print(F("Could not deserialize hmstatus event: "));
    Serial.println(err.c_str());
    return;
  }

  updateProxyFromJson(doc);
}

void HeaterMeterClient::handlePidInt(char* data)
{
  // {"b":0.00,"p":5.27,"i":96.32,"d":0.08,"t":-0.01}
  StaticJsonDocument<128> doc;
  DeserializationError err = deserializeJson(doc, data);
  if (err)
  {
    Serial.print(F("Could not deserialize pidint event: "));
    Serial.println(err.c_str());
    return;
  }

  state.Output.Internals.LastUpdate = millis();
  state.Output.Internals.P = doc["p"];
  state.Output.Internals.I = doc["i"];
  state.Output.Internals.D = doc["d"];
  state.Output.Internals.dT = doc["t"];

  setNotifyPending(nptPidInt);
}

void HeaterMeterClient::handlePeaks(char* data)
{
  // There's more JSON than this, need a real capture with H and L peaks
  //{"H":{"trend":1,"time":1613317715,"val":72.299999999999997},
  // "C":{"trend":0},
  // "outhist":[82892,9217,3389,3022,2675,2592,2861,2366,2167,2184,2133,2106,2375,2206,1569,1518,1539,1502,1493,1545,1550,1597,1624,1665,3045,542715],
  // "updatecnt":[0,18475,683548,30,0],"L":{"trend":-1,"time":1613317715,"val":72.299999999999997},
  // "mode":3}
}

void HeaterMeterClient::handleServerSentEvent(char* data)
{
  //Serial.print(_eventType); Serial.write('='); Serial.print(data);

  if (strcmp(_eventType, "hmstatus") == 0)
    handleHmStatus(data);
  else if (strcmp(_eventType, "pidint") == 0)
    handlePidInt(data);
  else if (strcmp(_eventType, "peaks") == 0)
    handlePeaks(data);
  else
  {
    //Serial.print(F("Unhandled server-sent event type: "));
    //Serial.println(_eventType);
  }
}

void HeaterMeterClient::handleServerSentLine(void)
{
  if (strncmp(_lineBuffer, "event: ", 7) == 0)
    strlcpy(_eventType, _lineBuffer + 7, _lineBufferPos - 6 - 1);
  else if (strncmp(_lineBuffer, "data: ", 6) == 0)
    handleServerSentEvent(_lineBuffer + 6);
  else if (strncmp(_lineBuffer, "retry: ", 7) == 0)
    _reconnectDelay = constrain(atoi(_lineBuffer + 7), 1000, 120*1000);
//  else
//    Serial.print(_lineBuffer);
}

void HeaterMeterClient::onData_cb(void* data, size_t len)
{
  //hexdump((char*)data, len);
  char* pos = (char*)data;
  _lastClientActivity = millis();

  if (_protocolState == hpsRequestSent)
  {
    _lineBufferPos = 0;
    setProtocolState(hpsHeaders);
  }

  while (len > 0 && readLine(&pos, &len))
  {
    switch (_protocolState)
    {
    case hpsHeaders:
      // Line will be blank if we've read the last header
      if (strcmp(_lineBuffer, "\r\n") == 0)
        setProtocolState(hpsChunk);
      //else
      //  Serial.print(g_LineBuffer);
      break;

    case hpsChunk:
      _expectedChunkSize = strtol(_lineBuffer, NULL, 16);
      setProtocolState(hpsChunkData);
      //Serial.print("Expecting chunk size "); Serial.println(_expectedChunkSize, DEC);
      break;

    case hpsChunkData:
      if (_expectedChunkSize == 0) // LineBuffer should be 0x0d 0x0a we can just assume
      {
        setProtocolState(hpsChunk);
      }
      else
      {
        _expectedChunkSize -= _lineBufferPos;
        if (_lineBufferPos > 1) // a server-sent event ends in \n\n so there will be a blank line
          handleServerSentLine();
      }
      break;

    default:
      len = 0; // ERROR
    } /* hmProtocolState */

    _lineBufferPos = 0;
  }  /* while len */
}

void HeaterMeterClient::onConnect_cb(void)
{
  setProtocolState(hpsConnected);
  _lastClientActivity = millis();
}

void HeaterMeterClient::onDisconnect_cb(void)
{
  setProtocolState(hpsReconnectDelay);
  _lastClientActivity = millis();
  state.clear();
}

void HeaterMeterClient::onError_cb(err_t err)
{
  if (onError)
    onError(err);
}

void HeaterMeterClient::clientConnect(void)
{
  _client.onData(&asynctcp_onData, this);
  _client.onConnect(&asynctcp_onConnect, this);
  _client.onDisconnect(&asynctcp_onDisconnect, this);
  _client.onError(&asynctcp_onError, this);
  // data should be received every 2-5s
  _client.setRxTimeout(CLIENT_HTTP_TIMEOUT);
  _client.connect(_host, 80);

  setProtocolState(hpsConnecting);
}

void HeaterMeterClient::clientSendRequest(void)
{
  if (_client.canSend())
  {
    _client.write("GET /luci/lm/stream HTTP/1.1\r\n\r\n");
    setProtocolState(hpsRequestSent);
    _lastClientActivity = millis();
  }
}

void HeaterMeterClient::clientCheckTimeout(void)
{
  if ((millis() - _lastClientActivity) > CLIENT_HTTP_TIMEOUT)
    _client.close();
}

void HeaterMeterClient::discover(void)
{
  _lastClientActivity = millis();
  // If there is an IP then there's no need to discover
  if (strlen(_host) > 0)
  {
    setProtocolState(hpsDisconnected);
    return;
  }

  // Request the JSON device list from HeaterMeter LIVE and use its best guess at the IP
  HTTPClient http;
  WiFiClient client;
  http.useHTTP10(true);
  http.begin(client, "http://heatermeter.com/devices/?fmt=json");
  int responseCode = http.GET();
  if (responseCode == 200)
  {
    // Use a JSON filter to only parse the "guess" address field
    StaticJsonDocument<32> filter;
    filter["guess"] = true;
    // Deserialize from HTTP stream
    StaticJsonDocument<64> doc;
    DeserializationError err = deserializeJson(doc, http.getStream(), DeserializationOption::Filter(filter));
    http.end();
    if (err)
    {
      Serial.print(F("Could not deserialize JSON discover: "));
      Serial.println(err.c_str());
      goto discoverError;
    }

    const JsonVariant& guess = doc["guess"];
    if (guess.isNull())
    {
      Serial.print(F("No HeaterMeter discovered"));
      goto discoverError;
    }

    // Found IP
    strlcpy(_host, guess, sizeof(_host));
    setProtocolState(hpsDisconnected);
    return;
  }
  else
  {
    Serial.print(F("Discover failed: "));
    Serial.println(responseCode, DEC);
    http.end();
  }

discoverError:
  setProtocolState(hpsReconnectDelay);
}

void HeaterMeterClient::setNotifyPending(NotifyPendingType npt)
{
  _notifyPending |= 1 << static_cast<uint8_t>(npt);
}

bool HeaterMeterClient::getNotifyPending(NotifyPendingType npt) const
{
  return _notifyPending & (1 << static_cast<uint8_t>(npt));
}

void HeaterMeterClient::clearNotifyPending(void)
{
  _notifyPending = 0;
}

/* Return true if state actually changed */
bool HeaterMeterClient::setProtocolState(HmclientProtocolState hps)
{
  if (_protocolState == hps)
    return false;
  //Serial.print(F("State=")); Serial.println(static_cast<uint8_t>(hps), DEC);
  // Notify before change in case the client needs to know the old state
  if (onProtocolStateChange)
    onProtocolStateChange(hps);

  HmclientProtocolState oldState = _protocolState;
  _protocolState = hps;

  switch (_protocolState)
  {
  case hpsNoNetwork: if (oldState > hpsNoNetwork && onWifiDisconnect) onWifiDisconnect(); break;
  case hpsDiscover: if (oldState == hpsNoNetwork && onWifiConnect) onWifiConnect(); break;
  case hpsReconnectDelay:if (oldState > hpsConnecting && onDisconnect) onDisconnect(); break;
  case hpsConnected: if (onConnect) onConnect(); break;
  case hpsNone:
  case hpsDisconnected:
  case hpsConnecting:
  case hpsRequestSent:
  case hpsHeaders:
  case hpsChunk:
  case hpsChunkData:
    break;
  }

  return true;
}

void HeaterMeterClient::update(void)
{
  if (WiFi.status() != WL_CONNECTED)
  {
    setProtocolState(hpsNoNetwork);
    return;
  }

  switch (_protocolState)
  {
  case hpsNone:
  case hpsNoNetwork:
    setProtocolState(hpsDiscover);
    break;
  case hpsDiscover:
    discover();
    break;
  case hpsReconnectDelay:
    if (millis() - _lastClientActivity > _reconnectDelay)
      setProtocolState(hpsDiscover);
    break;
  case hpsDisconnected:
    state.clear();
    clientConnect();
    break;
  case hpsConnecting:
    break;
  case hpsConnected:
    clientSendRequest();
    break;
  case hpsRequestSent:
  case hpsHeaders:
  case hpsChunk:
  case hpsChunkData:
    clientCheckTimeout();
    break;
  } /* switch */

  // Send notifies which were deferred to save stack space
  if (onHmStatus && getNotifyPending(nptHmStatus))
    onHmStatus();
  if (onPidInt && getNotifyPending(nptPidInt))
    onPidInt();
  clearNotifyPending();
}<commit_msg>[hmc] Add idle timeout message<commit_after>#include "HeaterMeterClient.h"
#if defined(ESP8266)
  #include <ESP8266HTTPClient.h>
#else
  #include <HTTPClient.h>
#endif

#define CLIENT_HTTP_TIMEOUT 6000

//static void hexdump(char* data, size_t len)
//{
//  while (len > 0)
//  {
//    uint8_t linepos = 0;
//    char* linestart = data;
//    // Binary part
//    while (len > 0 && linepos < 16)
//    {
//      if (*data < 0x0f)
//        Serial.write('0');
//      Serial.print(*data, HEX);
//      Serial.write(' ');
//      ++data;
//      ++linepos;
//      --len;
//    }
//
//    // Spacer to align last line
//    for (uint8_t i = linepos; i < 16; ++i)
//      Serial.print("   ");
//
//    // ASCII part
//    for (uint8_t i = 0; i < linepos; ++i)
//      Serial.write((linestart[i] < ' ') ? '.' : linestart[i]);
//    Serial.write('\n');
//  }
//}

void HeaterMeterClientProbe::clear(void)
{
  Name[0] = '\0';
  //float Temperature;
  HasTemperature = false;
  AlarmLow = 0;;
  AlarmHigh = 0;
  AlarmRinging = ' ';
  //float TemperatureDph;
  HasTemperatureDph = false;
}

void HeaterMeterClientPidInternals::clear(void)
{
  LastUpdate = 0;

  P = 0.0f;
  I = 0.0f;
  D = 0.0f;
  dT = 0.0f;
}

void HeaterMeterClientPidOutput::clear(void)
{
  Enabled = false;
  Current = 0;
  Fan = 0;
  Servo = 0;

  Internals.clear();
}

void HeaterMeterClientPid::clear(void)
{
  UpdateUtc = 0;
  for (uint8_t i = 0; i < TEMP_COUNT; ++i)
    Probes[i].clear();
  Output.clear();
}

HeaterMeterClient::HeaterMeterClient(const char* host) :
  _protocolState(hpsNone), _reconnectDelay(60000)
{
  strlcpy(_host, host, sizeof(_host));
}

void HeaterMeterClient::asynctcp_onData(void* arg, AsyncClient* c, void* data, size_t len)
{
  ((HeaterMeterClient*)arg)->onData_cb(data, len);
}

void HeaterMeterClient::asynctcp_onConnect(void* arg, AsyncClient* c)
{
  ((HeaterMeterClient*)arg)->onConnect_cb();
}

void HeaterMeterClient::asynctcp_onDisconnect(void* arg, AsyncClient* c)
{
  ((HeaterMeterClient*)arg)->onDisconnect_cb();
}

void HeaterMeterClient::asynctcp_onError(void* arg, AsyncClient* c, err_t err)
{
  ((HeaterMeterClient*)arg)->onError_cb(err);
}

bool HeaterMeterClient::readLine(char** pos, size_t* len)
{
  // Looking at HTTP lines each ending in [0x0d] 0x0a
  bool wasNl = false;
  while ((*len > 0) && (_lineBufferPos < sizeof(_lineBuffer) - 1) & !wasNl)
  {
    _lineBuffer[_lineBufferPos++] = **pos;
    wasNl = (**pos == 0x0a);
    ++* pos;
    --* len;
  } /* while building line */

  _lineBuffer[_lineBufferPos] = '\0';
  return wasNl;
}

void HeaterMeterClient::updateProxyFromJson(JsonDocument& doc)
{
  for (uint8_t i = 0; i < TEMP_COUNT; ++i)
  {
    HeaterMeterClientProbe& p = state.Probes[i];
    const JsonObject& jp = doc["temps"][i];

    // Name
    strlcpy(p.Name, jp["n"], sizeof(p.Name));
    // Temperature
    const JsonVariant& t = jp["c"];
    p.HasTemperature = !t.isNull();
    if (p.HasTemperature)
      p.Temperature = t;
    else
      p.Temperature = 0.0f;
    // DPH
    p.HasTemperatureDph = jp.containsKey("dph");
    if (p.HasTemperatureDph)
      p.TemperatureDph = jp["dph"];
    else
      p.TemperatureDph = 0.0f;
    // Alarms
    p.AlarmHigh = jp["a"]["h"];
    p.AlarmLow = jp["a"]["l"];
    p.AlarmRinging = jp["a"]["r"].isNull() ? ' ' : jp["a"]["r"];
  }

  // Output
  const JsonObject& output = doc["fan"];
  state.Output.Current = output["c"];
  state.Output.Fan = output["f"];
  state.Output.Servo = output["s"];
  state.Output.Enabled = !doc["set"].isNull();

  // Setpoint
  if (state.Output.Enabled)
    state.Setpoint = doc["set"];
  else
    state.Setpoint = 0;

  // Lid
  state.LidCountdown = doc["lid"];

  // Main
  state.UpdateUtc = doc["time"];

  setNotifyPending(nptHmStatus);
}

void HeaterMeterClient::handleHmStatus(char* data)
{
  StaticJsonDocument<1024> doc;
  DeserializationError err = deserializeJson(doc, data);
  if (err)
  {
    Serial.print(F("Could not deserialize hmstatus event: "));
    Serial.println(err.c_str());
    return;
  }

  updateProxyFromJson(doc);
}

void HeaterMeterClient::handlePidInt(char* data)
{
  // {"b":0.00,"p":5.27,"i":96.32,"d":0.08,"t":-0.01}
  StaticJsonDocument<128> doc;
  DeserializationError err = deserializeJson(doc, data);
  if (err)
  {
    Serial.print(F("Could not deserialize pidint event: "));
    Serial.println(err.c_str());
    return;
  }

  state.Output.Internals.LastUpdate = millis();
  state.Output.Internals.P = doc["p"];
  state.Output.Internals.I = doc["i"];
  state.Output.Internals.D = doc["d"];
  state.Output.Internals.dT = doc["t"];

  setNotifyPending(nptPidInt);
}

void HeaterMeterClient::handlePeaks(char* data)
{
  // There's more JSON than this, need a real capture with H and L peaks
  //{"H":{"trend":1,"time":1613317715,"val":72.299999999999997},
  // "C":{"trend":0},
  // "outhist":[82892,9217,3389,3022,2675,2592,2861,2366,2167,2184,2133,2106,2375,2206,1569,1518,1539,1502,1493,1545,1550,1597,1624,1665,3045,542715],
  // "updatecnt":[0,18475,683548,30,0],"L":{"trend":-1,"time":1613317715,"val":72.299999999999997},
  // "mode":3}
}

void HeaterMeterClient::handleServerSentEvent(char* data)
{
  //Serial.print(_eventType); Serial.write('='); Serial.print(data);

  if (strcmp(_eventType, "hmstatus") == 0)
    handleHmStatus(data);
  else if (strcmp(_eventType, "pidint") == 0)
    handlePidInt(data);
  else if (strcmp(_eventType, "peaks") == 0)
    handlePeaks(data);
  else
  {
    //Serial.print(F("Unhandled server-sent event type: "));
    //Serial.println(_eventType);
  }
}

void HeaterMeterClient::handleServerSentLine(void)
{
  if (strncmp(_lineBuffer, "event: ", 7) == 0)
    strlcpy(_eventType, _lineBuffer + 7, _lineBufferPos - 6 - 1);
  else if (strncmp(_lineBuffer, "data: ", 6) == 0)
    handleServerSentEvent(_lineBuffer + 6);
  else if (strncmp(_lineBuffer, "retry: ", 7) == 0)
    _reconnectDelay = constrain(atoi(_lineBuffer + 7), 1000, 120*1000);
//  else
//    Serial.print(_lineBuffer);
}

void HeaterMeterClient::onData_cb(void* data, size_t len)
{
  //hexdump((char*)data, len);
  char* pos = (char*)data;
  _lastClientActivity = millis();

  if (_protocolState == hpsRequestSent)
  {
    _lineBufferPos = 0;
    setProtocolState(hpsHeaders);
  }

  while (len > 0 && readLine(&pos, &len))
  {
    switch (_protocolState)
    {
    case hpsHeaders:
      // Line will be blank if we've read the last header
      if (strcmp(_lineBuffer, "\r\n") == 0)
        setProtocolState(hpsChunk);
      //else
      //  Serial.print(g_LineBuffer);
      break;

    case hpsChunk:
      _expectedChunkSize = strtol(_lineBuffer, NULL, 16);
      setProtocolState(hpsChunkData);
      //Serial.print("Expecting chunk size "); Serial.println(_expectedChunkSize, DEC);
      break;

    case hpsChunkData:
      if (_expectedChunkSize == 0) // LineBuffer should be 0x0d 0x0a we can just assume
      {
        setProtocolState(hpsChunk);
      }
      else
      {
        _expectedChunkSize -= _lineBufferPos;
        if (_lineBufferPos > 1) // a server-sent event ends in \n\n so there will be a blank line
          handleServerSentLine();
      }
      break;

    default:
      len = 0; // ERROR
    } /* hmProtocolState */

    _lineBufferPos = 0;
  }  /* while len */
}

void HeaterMeterClient::onConnect_cb(void)
{
  setProtocolState(hpsConnected);
  _lastClientActivity = millis();
}

void HeaterMeterClient::onDisconnect_cb(void)
{
  setProtocolState(hpsReconnectDelay);
  _lastClientActivity = millis();
  state.clear();
}

void HeaterMeterClient::onError_cb(err_t err)
{
  if (onError)
    onError(err);
}

void HeaterMeterClient::clientConnect(void)
{
  _client.onData(&asynctcp_onData, this);
  _client.onConnect(&asynctcp_onConnect, this);
  _client.onDisconnect(&asynctcp_onDisconnect, this);
  _client.onError(&asynctcp_onError, this);
  // data should be received every 2-5s
  _client.setRxTimeout(CLIENT_HTTP_TIMEOUT);
  _client.connect(_host, 80);

  setProtocolState(hpsConnecting);
}

void HeaterMeterClient::clientSendRequest(void)
{
  if (_client.canSend())
  {
    _client.write("GET /luci/lm/stream HTTP/1.1\r\n\r\n");
    setProtocolState(hpsRequestSent);
    _lastClientActivity = millis();
  }
}

void HeaterMeterClient::clientCheckTimeout(void)
{
  if ((millis() - _lastClientActivity) > CLIENT_HTTP_TIMEOUT)
  {
    Serial.println(F("Idle timeout"));
    _client.close();
  }
}

void HeaterMeterClient::discover(void)
{
  _lastClientActivity = millis();
  // If there is an IP then there's no need to discover
  if (strlen(_host) > 0)
  {
    setProtocolState(hpsDisconnected);
    return;
  }

  // Request the JSON device list from HeaterMeter LIVE and use its best guess at the IP
  HTTPClient http;
  WiFiClient client;
  http.useHTTP10(true);
  http.begin(client, "http://heatermeter.com/devices/?fmt=json");
  int responseCode = http.GET();
  if (responseCode == 200)
  {
    // Use a JSON filter to only parse the "guess" address field
    StaticJsonDocument<32> filter;
    filter["guess"] = true;
    // Deserialize from HTTP stream
    StaticJsonDocument<64> doc;
    DeserializationError err = deserializeJson(doc, http.getStream(), DeserializationOption::Filter(filter));
    http.end();
    if (err)
    {
      Serial.print(F("Could not deserialize JSON discover: "));
      Serial.println(err.c_str());
      goto discoverError;
    }

    const JsonVariant& guess = doc["guess"];
    if (guess.isNull())
    {
      Serial.print(F("No HeaterMeter discovered"));
      goto discoverError;
    }

    // Found IP
    strlcpy(_host, guess, sizeof(_host));
    setProtocolState(hpsDisconnected);
    return;
  }
  else
  {
    Serial.print(F("Discover failed: "));
    Serial.println(responseCode, DEC);
    http.end();
  }

discoverError:
  setProtocolState(hpsReconnectDelay);
}

void HeaterMeterClient::setNotifyPending(NotifyPendingType npt)
{
  _notifyPending |= 1 << static_cast<uint8_t>(npt);
}

bool HeaterMeterClient::getNotifyPending(NotifyPendingType npt) const
{
  return _notifyPending & (1 << static_cast<uint8_t>(npt));
}

void HeaterMeterClient::clearNotifyPending(void)
{
  _notifyPending = 0;
}

/* Return true if state actually changed */
bool HeaterMeterClient::setProtocolState(HmclientProtocolState hps)
{
  if (_protocolState == hps)
    return false;
  //Serial.print(F("State=")); Serial.println(static_cast<uint8_t>(hps), DEC);
  // Notify before change in case the client needs to know the old state
  if (onProtocolStateChange)
    onProtocolStateChange(hps);

  HmclientProtocolState oldState = _protocolState;
  _protocolState = hps;

  switch (_protocolState)
  {
  case hpsNoNetwork: if (oldState > hpsNoNetwork && onWifiDisconnect) onWifiDisconnect(); break;
  case hpsDiscover: if (oldState == hpsNoNetwork && onWifiConnect) onWifiConnect(); break;
  case hpsReconnectDelay:if (oldState > hpsConnecting && onDisconnect) onDisconnect(); break;
  case hpsConnected: if (onConnect) onConnect(); break;
  case hpsNone:
  case hpsDisconnected:
  case hpsConnecting:
  case hpsRequestSent:
  case hpsHeaders:
  case hpsChunk:
  case hpsChunkData:
    break;
  }

  return true;
}

void HeaterMeterClient::update(void)
{
  if (WiFi.status() != WL_CONNECTED)
  {
    setProtocolState(hpsNoNetwork);
    return;
  }

  switch (_protocolState)
  {
  case hpsNone:
  case hpsNoNetwork:
    setProtocolState(hpsDiscover);
    break;
  case hpsDiscover:
    discover();
    break;
  case hpsReconnectDelay:
    if (millis() - _lastClientActivity > _reconnectDelay)
      setProtocolState(hpsDiscover);
    break;
  case hpsDisconnected:
    state.clear();
    clientConnect();
    break;
  case hpsConnecting:
    break;
  case hpsConnected:
    clientSendRequest();
    break;
  case hpsRequestSent:
  case hpsHeaders:
  case hpsChunk:
  case hpsChunkData:
    clientCheckTimeout();
    break;
  } /* switch */

  // Send notifies which were deferred to save stack space
  if (onHmStatus && getNotifyPending(nptHmStatus))
    onHmStatus();
  if (onPidInt && getNotifyPending(nptPidInt))
    onPidInt();
  clearNotifyPending();
}<|endoftext|>


<commit_before>#include "umundo/core.h"
#include <iostream>

using namespace umundo;

bool testRecursiveMutex() {
	Mutex mutex;
	UMUNDO_LOCK(mutex);
	if(!mutex.tryLock()) {
		LOG_ERR("tryLock should be possible from within the same thread");
		assert(false);
	}
	UMUNDO_LOCK(mutex);
	// can we unlock it as well?
	UMUNDO_UNLOCK(mutex);
	UMUNDO_UNLOCK(mutex);
	return true;
}

static Mutex testMutex;
bool testThreads() {
	class Thread1 : public Thread {
		void run() {
			if(testMutex.tryLock()) {
				LOG_ERR("tryLock should return false with a mutex locked in another thread");
				assert(false);
			}
			UMUNDO_LOCK(testMutex); // blocks
			Thread::sleepMs(50);
			UMUNDO_UNLOCK(testMutex);
			Thread::sleepMs(100);
		}
	};

	/**
	 * tl=tryLock, l=lock, u=unlock b=block, sN=sleep(N)
	 * thread1:        start tl l  l s50      u
	 *    main: start l s50       u s20  tl    l join
	 *    t->          0         50     70   100
	 */

	UMUNDO_LOCK(testMutex);
	Thread1 thread1;
	thread1.start();
	Thread::sleepMs(50); // thread1 will trylock and block on lock
	UMUNDO_UNLOCK(testMutex);  // unlock
	Thread::sleepMs(20); // yield cpu and sleep
	// thread1 sleeps with lock on mutex
	if(testMutex.tryLock()) {
		LOG_ERR("tryLock should return false with a mutex locked in another thread");
		assert(false);
	}
	UMUNDO_LOCK(testMutex);    // thread1 will unlock and sleep
	thread1.join();      // join with thread1
	if(thread1.isStarted()) {
		LOG_ERR("thread still running after join");
		assert(false);
	}
	return true;
}

static Monitor testMonitor;
static int passedMonitor = 0;
bool testMonitors() {
	struct TestThread : public Thread {
		int _ms;
		TestThread(int ms) : _ms(ms) {}
		void run() {
			testMonitor.wait();
			Thread::sleepMs(10); // avoid clash with other threads
			passedMonitor++;
		}
	};

	TestThread thread1(0);
	TestThread thread2(5);
	TestThread thread3(10);

	for (int i = 0; i < 10; i++) {
		passedMonitor = 0;

		// all will block on monitor
		thread1.start();
		thread2.start();
		thread3.start();
		Thread::sleepMs(5); // give threads a chance to run into wait
		if(passedMonitor != 0) {
			LOG_ERR("%d threads already passed the monitor", passedMonitor);
			assert(false);
		}
		UMUNDO_SIGNAL(testMonitor); // signal a single thread
		Thread::sleepMs(15); // thread will increase passedMonitor
		if(passedMonitor != 1) {
			LOG_ERR("Expected 1 threads to pass the monitor, but %d did", passedMonitor);
			assert(false);
		}
		UMUNDO_BROADCAST(testMonitor); // signal all other threads
		Thread::sleepMs(15);

		if (thread1.isStarted() || thread2.isStarted() || thread3.isStarted()) {
			LOG_ERR("Threads ran to completion but still insist on being started");
			assert(false);
		}
	}
	return true;
}

static Monitor testTimedMonitor;
static int passedTimedMonitor = 0;
bool testTimedMonitors() {
	struct TestThread : public Thread {
		int _ms;
		TestThread(int ms) : _ms(ms) {}
		void run() {
			testTimedMonitor.wait(_ms);
			passedTimedMonitor++;
		}
	};

	TestThread thread1(15);
	TestThread thread2(0); // waits forever
	TestThread thread3(0); // waits forever
	TestThread thread4(0); // waits forever
	TestThread thread5(0); // waits forever

	for (int i = 0; i < 50; i++) {
		// test waiting for a given time
		passedTimedMonitor = 0;
		thread1.start(); // wait for 15ms at mutex before resuming
		Thread::sleepMs(5);
		assert(passedTimedMonitor == 0); // thread1 should not have passed
		Thread::sleepMs(25);
		assert(passedTimedMonitor == 1); // thread1 should have passed
		assert(!thread1.isStarted());

		// test signalling a set of threads
		passedTimedMonitor = 0;
		thread2.start();
		thread3.start();
		thread4.start();
		thread5.start();
		Thread::sleepMs(5);
		testTimedMonitor.signal(2); // signal 2 threads
		Thread::sleepMs(10);
		assert(passedTimedMonitor == 2);
		testTimedMonitor.signal(1); // signal another thread
		Thread::sleepMs(5);
		assert(passedTimedMonitor == 3);
		testTimedMonitor.broadcast(); // signal last thread
		Thread::sleepMs(5);
		assert(passedTimedMonitor == 4);
		assert(!thread2.isStarted() && !thread3.isStarted() && !thread4.isStarted() && !thread5.isStarted());

		// test timed and unlimited waiting
		passedTimedMonitor = 0;
		thread1.start();
		thread2.start(); // with another thread
		thread3.start(); // with another thread
		Thread::sleepMs(5);
		testTimedMonitor.signal(); // explicit signal
		Thread::sleepMs(10);
		assert(passedTimedMonitor == 1);
		// wo do not know which thread passed
		assert(!thread1.isStarted() || !thread2.isStarted() || !thread3.isStarted());
		if (thread1.isStarted()) {
			// thread1 is still running, just wait
			Thread::sleepMs(10);
			assert(passedTimedMonitor == 2);
		}
		testTimedMonitor.broadcast(); // explicit signal
		Thread::sleepMs(5);
		assert(passedTimedMonitor == 3);
		assert(!thread1.isStarted() && !thread2.isStarted() && !thread3.isStarted());

		// test signalling prior to waiting
		passedTimedMonitor = 0;
		testTimedMonitor.signal();
		thread1.start();
		Thread::sleepMs(5);
		thread2.start();
		Thread::sleepMs(5);
		assert(passedTimedMonitor == 1);
		assert(!thread1.isStarted());
		assert(thread2.isStarted());
		testTimedMonitor.signal();
		Thread::sleepMs(5);
		assert(passedTimedMonitor == 2);

		assert(!thread1.isStarted());
		assert(!thread2.isStarted());
		assert(!thread3.isStarted());

	}
	return true;
}

class FooTracer : public Traceable, public Thread {
	void run() {
		while(isStarted()) {
			trace("This is foo");
			Thread::sleepMs(20);
		}
	}
};

bool testTracing() {
	FooTracer* tr1 = new FooTracer();
	FooTracer* tr2 = new FooTracer();
	FooTracer* tr3 = new FooTracer();

	tr1->setTraceFile("trace.txt");
	tr2->setTraceFile("trace.txt");
	tr3->setTraceFile("trace.txt");

	tr1->start();
	tr2->start();
	tr3->start();

	Thread::sleepMs(100);
	delete tr1;
	delete tr2;
	delete tr3;

	return true;
}

int main(int argc, char** argv) {
	if(!testTracing())
		return EXIT_FAILURE;
	if(!testRecursiveMutex())
		return EXIT_FAILURE;
	if(!testThreads())
		return EXIT_FAILURE;
	if(!testMonitors())
		return EXIT_FAILURE;
	if(!testTimedMonitors())
		return EXIT_FAILURE;
}
<commit_msg>increased timeouts<commit_after>#include "umundo/core.h"
#include <iostream>

using namespace umundo;

bool testRecursiveMutex() {
	Mutex mutex;
	UMUNDO_LOCK(mutex);
	if(!mutex.tryLock()) {
		LOG_ERR("tryLock should be possible from within the same thread");
		assert(false);
	}
	UMUNDO_LOCK(mutex);
	// can we unlock it as well?
	UMUNDO_UNLOCK(mutex);
	UMUNDO_UNLOCK(mutex);
	return true;
}

static Mutex testMutex;
bool testThreads() {
	class Thread1 : public Thread {
		void run() {
			if(testMutex.tryLock()) {
				LOG_ERR("tryLock should return false with a mutex locked in another thread");
				assert(false);
			}
			UMUNDO_LOCK(testMutex); // blocks
			Thread::sleepMs(50);
			UMUNDO_UNLOCK(testMutex);
			Thread::sleepMs(100);
		}
	};

	/**
	 * tl=tryLock, l=lock, u=unlock b=block, sN=sleep(N)
	 * thread1:        start tl l  l s50      u
	 *    main: start l s50       u s20  tl    l join
	 *    t->          0         50     70   100
	 */

	UMUNDO_LOCK(testMutex);
	Thread1 thread1;
	thread1.start();
	Thread::sleepMs(50); // thread1 will trylock and block on lock
	UMUNDO_UNLOCK(testMutex);  // unlock
	Thread::sleepMs(20); // yield cpu and sleep
	// thread1 sleeps with lock on mutex
	if(testMutex.tryLock()) {
		LOG_ERR("tryLock should return false with a mutex locked in another thread");
		assert(false);
	}
	UMUNDO_LOCK(testMutex);    // thread1 will unlock and sleep
	thread1.join();      // join with thread1
	if(thread1.isStarted()) {
		LOG_ERR("thread still running after join");
		assert(false);
	}
	return true;
}

static Monitor testMonitor;
static int passedMonitor = 0;
bool testMonitors() {
	struct TestThread : public Thread {
		int _ms;
		TestThread(int ms) : _ms(ms) {}
		void run() {
			testMonitor.wait();
			Thread::sleepMs(10); // avoid clash with other threads
			passedMonitor++;
		}
	};

	TestThread thread1(0);
	TestThread thread2(5);
	TestThread thread3(10);

	for (int i = 0; i < 10; i++) {
		passedMonitor = 0;

		// all will block on monitor
		thread1.start();
		thread2.start();
		thread3.start();
		Thread::sleepMs(5); // give threads a chance to run into wait
		if(passedMonitor != 0) {
			LOG_ERR("%d threads already passed the monitor", passedMonitor);
			assert(false);
		}
		UMUNDO_SIGNAL(testMonitor); // signal a single thread
		Thread::sleepMs(15); // thread will increase passedMonitor
		if(passedMonitor != 1) {
			LOG_ERR("Expected 1 threads to pass the monitor, but %d did", passedMonitor);
			assert(false);
		}
		UMUNDO_BROADCAST(testMonitor); // signal all other threads
		Thread::sleepMs(15);

		if (thread1.isStarted() || thread2.isStarted() || thread3.isStarted()) {
			LOG_ERR("Threads ran to completion but still insist on being started");
			assert(false);
		}
	}
	return true;
}

static Monitor testTimedMonitor;
static int passedTimedMonitor = 0;
bool testTimedMonitors() {
	struct TestThread : public Thread {
		int _ms;
		TestThread(int ms) : _ms(ms) {}
		void run() {
			testTimedMonitor.wait(_ms);
			passedTimedMonitor++;
		}
	};

	TestThread thread1(40);
	TestThread thread2(0); // waits forever
	TestThread thread3(0); // waits forever
	TestThread thread4(0); // waits forever
	TestThread thread5(0); // waits forever

	for (int i = 0; i < 10; i++) {
		// test waiting for a given time
		passedTimedMonitor = 0;
		thread1.start(); // wait for 15ms at mutex before resuming
		Thread::sleepMs(10);
		assert(passedTimedMonitor == 0); // thread1 should not have passed
		Thread::sleepMs(60);
		assert(passedTimedMonitor == 1); // thread1 should have passed
		assert(!thread1.isStarted());

		// test signalling a set of threads
		passedTimedMonitor = 0;
		thread2.start();
		thread3.start();
		thread4.start();
		thread5.start();
		Thread::sleepMs(5);
		testTimedMonitor.signal(2); // signal 2 threads
		Thread::sleepMs(20);
		assert(passedTimedMonitor == 2);
		testTimedMonitor.signal(1); // signal another thread
		Thread::sleepMs(20);
		assert(passedTimedMonitor == 3);
		testTimedMonitor.broadcast(); // signal last thread
		Thread::sleepMs(20);
		assert(passedTimedMonitor == 4);
		assert(!thread2.isStarted() && !thread3.isStarted() && !thread4.isStarted() && !thread5.isStarted());

		// test timed and unlimited waiting
		passedTimedMonitor = 0;
		thread1.start();
		thread2.start(); // with another thread
		thread3.start(); // with another thread
		Thread::sleepMs(5);
		testTimedMonitor.signal(); // explicit signal
		Thread::sleepMs(20);
		assert(passedTimedMonitor == 1);
		// wo do not know which thread passed
		assert(!thread1.isStarted() || !thread2.isStarted() || !thread3.isStarted());
		if (thread1.isStarted()) {
			// thread1 is still running, just wait
			Thread::sleepMs(60);
			assert(passedTimedMonitor == 2);
		}
		testTimedMonitor.broadcast(); // explicit signal
		Thread::sleepMs(20);
		assert(passedTimedMonitor == 3);
		assert(!thread1.isStarted() && !thread2.isStarted() && !thread3.isStarted());

		// test signalling prior to waiting
		passedTimedMonitor = 0;
		testTimedMonitor.signal();
		thread1.start();
		Thread::sleepMs(10);
		thread2.start();
		Thread::sleepMs(10);
		assert(passedTimedMonitor == 1);
		assert(!thread1.isStarted());
		assert(thread2.isStarted());
		testTimedMonitor.signal();
		Thread::sleepMs(20);
		assert(passedTimedMonitor == 2);

		assert(!thread1.isStarted());
		assert(!thread2.isStarted());
		assert(!thread3.isStarted());

	}
	return true;
}

class FooTracer : public Traceable, public Thread {
	void run() {
		while(isStarted()) {
			trace("This is foo");
			Thread::sleepMs(20);
		}
	}
};

bool testTracing() {
	FooTracer* tr1 = new FooTracer();
	FooTracer* tr2 = new FooTracer();
	FooTracer* tr3 = new FooTracer();

	tr1->setTraceFile("trace.txt");
	tr2->setTraceFile("trace.txt");
	tr3->setTraceFile("trace.txt");

	tr1->start();
	tr2->start();
	tr3->start();

	Thread::sleepMs(100);
	delete tr1;
	delete tr2;
	delete tr3;

	return true;
}

int main(int argc, char** argv) {
	if(!testTracing())
		return EXIT_FAILURE;
	if(!testRecursiveMutex())
		return EXIT_FAILURE;
	if(!testThreads())
		return EXIT_FAILURE;
	if(!testMonitors())
		return EXIT_FAILURE;
	if(!testTimedMonitors())
		return EXIT_FAILURE;
}
<|endoftext|>


<commit_before>#ifndef DICE_RANDOMOPERATION_H
#define DICE_RANDOMOPERATION_H

#include "../stdafx.hpp"

#include "IOperation.hpp"

class RandomOperation : public IOperation
{
    const int _lower, _upper;
    unsigned int _seed;
    bool _fixedSeed;

protected:
    /**
     * \brief Generates one random number from range specified in the
     * constructor.
     *
     * \return Unique pointer to RollResult storing one random number.
     *
     * \exception std::invalid_argument If constructor got bad arguments.
     * (Lower is less than 1 or/and less than upper)
     */
    std::unique_ptr<RollResult> execute();

public:
    // If seed is not given, it will randomize using time
    RandomOperation(int upper);
    RandomOperation(int lower, int upper);

    // Throws exception if lower >= upper
    std::unique_ptr<RollResult> evaluate();
    void changeSeed(unsigned newSeed);
};

#endif // DICE_RANDOMOPERATION_H
<commit_msg>Documentation for constructors.<commit_after>#ifndef DICE_RANDOMOPERATION_H
#define DICE_RANDOMOPERATION_H

#include "../stdafx.hpp"

#include "IOperation.hpp"

class RandomOperation : public IOperation
{
    const int _lower, _upper;
    unsigned int _seed;
    bool _fixedSeed;

protected:
    /**
     * \brief Generates one random number from range specified in the
     * constructor.
     *
     * \return Unique pointer to RollResult storing one random number.
     *
     * \exception std::invalid_argument If constructor got bad arguments.
     * (Lower is less than 1 or/and less than upper)
     */
    std::unique_ptr<RollResult> execute();

public:
    // If seed is not given, it will randomize using time
    /**
     * Defines range for RandomOperation. Lower limit is 1, and upper is defined (both are inclusive).
     * by the user.
     *
     * \param upper [in] Upper limit (inclusive) in random range.
     */
    RandomOperation(int upper);
    /**
     * Defines range for RandomOperation. User can specify lower and upper limit (both inclusive).
     * for randomizing a number.
     *
     * \param lower Lower limit (inclusive) in random range.
     * \param upper Upper limit (inclusive) in random range.
     */
    RandomOperation(int lower, int upper);

    // Throws exception if lower >= upper
    std::unique_ptr<RollResult> evaluate();
    void changeSeed(unsigned newSeed);
};

#endif // DICE_RANDOMOPERATION_H
<|endoftext|>


<commit_before>//======================================================================
//-----------------------------------------------------------------------
/**
 * @file        iutest_genparams_from_file.hpp
 * @brief       iris unit test parameter generator from file
 *
 * @author      t.shirayanagi
 * @par         copyright
 * Copyright (C) 2015-2017, Takazumi Shirayanagi\n
 * This software is released under the new BSD License,
 * see LICENSE
*/
//-----------------------------------------------------------------------
//======================================================================
#ifndef INCG_IRIS_IUTEST_GENPARAMS_FROM_FILE_HPP_81CDA6E7_945B_4F72_873B_104264DF7132_
#define INCG_IRIS_IUTEST_GENPARAMS_FROM_FILE_HPP_81CDA6E7_945B_4F72_873B_104264DF7132_

#if IUTEST_HAS_CSVPARAMS

namespace iutest {
namespace detail
{

/**
 * @brief   csv ファイルからのパラメータ生成器
 * @tparam T    = パラメータ型
*/
template<typename T>
class iuCsvFileParamsGenerator : public iuValuesInParamsGenerator<T>
{
    typedef ::std::vector<T> params_t;
public:
    /**
     * @brief   コンストラクタ
     * @param [in]  path        = ファイルパス
     * @param [in]  delimiter   = 区切り文字
    */
    explicit iuCsvFileParamsGenerator(const ::std::string& path, char delimiter = ',')
        : iuValuesInParamsGenerator<T>(ReadParams(path, delimiter))
    {
    }

private:
    bool ToParam(const ::std::string&, T&) { return false; }

private:
    void AppendParams(params_t& params, const ::std::string& data)
    {
        if( StringIsBlank(data) )
        {
            return;
        }
        ::std::istringstream strm(data);
        T param;
        if( !(strm >> param) )
        {
            if( !ToParam(data, param) )
            {
                return;
            }
        }
        params.push_back(param);
    }
    params_t ReadParams(const ::std::string& path, char delimiter = ',')
    {
        params_t params;
        IFile* fp = detail::IFileSystem::New();
        if( fp != NULL )
        {
            if( fp->Open(path.c_str(), IFile::OpenRead) )
            {
                const ::std::string dataset = fp->ReadAll();
                ::std::string::size_type prev = 0;
                ::std::string::size_type pos = 0;
                while( pos = dataset.find(delimiter, prev), pos != ::std::string::npos )
                {
                    const ::std::string data = dataset.substr(prev, pos - prev);
                    AppendParams(params, data);
                    ++pos;
                    prev = pos;
                }
                AppendParams(params, dataset.substr(prev));
            }
            else
            {
                fprintf(stderr, "Unable to open file \"%s\".\n", path.c_str());
                fflush(stderr);
            }
            detail::IFileSystem::Free(fp);
        }
        IUTEST_CHECK_(!params.empty());
        return params;
    }
};

template<>inline bool iuCsvFileParamsGenerator<float>::ToParam(const ::std::string& data, float& param)
{
    param = std::stof(data);
    return true;
}

}   // end of namespace detail
}   // end of namespace iutest

#endif

#endif // INCG_IRIS_IUTEST_GENPARAMS_FROM_FILE_HPP_81CDA6E7_945B_4F72_873B_104264DF7132_
<commit_msg>fix before c++11 build error<commit_after>//======================================================================
//-----------------------------------------------------------------------
/**
 * @file        iutest_genparams_from_file.hpp
 * @brief       iris unit test parameter generator from file
 *
 * @author      t.shirayanagi
 * @par         copyright
 * Copyright (C) 2015-2017, Takazumi Shirayanagi\n
 * This software is released under the new BSD License,
 * see LICENSE
*/
//-----------------------------------------------------------------------
//======================================================================
#ifndef INCG_IRIS_IUTEST_GENPARAMS_FROM_FILE_HPP_81CDA6E7_945B_4F72_873B_104264DF7132_
#define INCG_IRIS_IUTEST_GENPARAMS_FROM_FILE_HPP_81CDA6E7_945B_4F72_873B_104264DF7132_

#if IUTEST_HAS_CSVPARAMS

namespace iutest {
namespace detail
{

/**
 * @brief   csv ファイルからのパラメータ生成器
 * @tparam T    = パラメータ型
*/
template<typename T>
class iuCsvFileParamsGenerator : public iuValuesInParamsGenerator<T>
{
    typedef ::std::vector<T> params_t;
public:
    /**
     * @brief   コンストラクタ
     * @param [in]  path        = ファイルパス
     * @param [in]  delimiter   = 区切り文字
    */
    explicit iuCsvFileParamsGenerator(const ::std::string& path, char delimiter = ',')
        : iuValuesInParamsGenerator<T>(ReadParams(path, delimiter))
    {
    }

private:
    bool ToParam(const ::std::string& data, T& param)
    {
        ::std::istringstream strm(data);
        if( strm >> param )
        {
            return true;
        }
        return false;
    }

private:
    void AppendParams(params_t& params, const ::std::string& data)
    {
        if( StringIsBlank(data) )
        {
            return;
        }
        T param;
        if( !ToParam(data, param) )
        {
            return;
        }
        params.push_back(param);
    }
    params_t ReadParams(const ::std::string& path, char delimiter = ',')
    {
        params_t params;
        IFile* fp = detail::IFileSystem::New();
        if( fp != NULL )
        {
            if( fp->Open(path.c_str(), IFile::OpenRead) )
            {
                const ::std::string dataset = fp->ReadAll();
                ::std::string::size_type prev = 0;
                ::std::string::size_type pos = 0;
                while( pos = dataset.find(delimiter, prev), pos != ::std::string::npos )
                {
                    const ::std::string data = dataset.substr(prev, pos - prev);
                    AppendParams(params, data);
                    ++pos;
                    prev = pos;
                }
                AppendParams(params, dataset.substr(prev));
            }
            else
            {
                fprintf(stderr, "Unable to open file \"%s\".\n", path.c_str());
                fflush(stderr);
            }
            detail::IFileSystem::Free(fp);
        }
        IUTEST_CHECK_(!params.empty());
        return params;
    }
};

template<>inline bool iuCsvFileParamsGenerator<float>::ToParam(const ::std::string& data, float& param)
{
#if IUTEST_HAS_CXX11
    param = std::stof(data);
    return true;
#else
    ::std::istringstream strm(data);
    if( !(strm >> param) )
    {
        param = static_cast<float>(atof(data.c_str()));
    }
    return true;
#endif
}

}   // end of namespace detail
}   // end of namespace iutest

#endif

#endif // INCG_IRIS_IUTEST_GENPARAMS_FROM_FILE_HPP_81CDA6E7_945B_4F72_873B_104264DF7132_
<|endoftext|>


<commit_before>//============================================================================
// Name        : ComputerVision.cpp
// Author      : Luke Hsiao, Clover Wu
// Version     :
// Copyright   : Copyright 2015 Team Vektor Krum
// Description : Hello World in C++, Ansi-style
//============================================================================

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace cv;
using namespace std;

 int main( int argc, char** argv )
 {
    VideoCapture cap(0); //capture the video from web cam

    if ( !cap.isOpened() )  // if not success, exit program
    {
         cout << "Cannot open the web cam" << endl;
         return -1;
    }

    namedWindow("Control", CV_WINDOW_AUTOSIZE); //create a window called "Control"

	int iLowH = 0;
	int iHighH = 179;

	int iLowS = 0;
	int iHighS = 255;

	int iLowV = 0;
	int iHighV = 255;

	//Create trackbars in "Control" window
	cvCreateTrackbar("LowH", "Control", &iLowH, 179); //Hue (0 - 179)
	cvCreateTrackbar("HighH", "Control", &iHighH, 179);

	cvCreateTrackbar("LowS", "Control", &iLowS, 255); //Saturation (0 - 255)
	cvCreateTrackbar("HighS", "Control", &iHighS, 255);

	cvCreateTrackbar("LowV", "Control", &iLowV, 255); //Value (0 - 255)
	cvCreateTrackbar("HighV", "Control", &iHighV, 255);

    while (true)
    {
        Mat imgOriginal;

        bool bSuccess = cap.read(imgOriginal); // read a new frame from video

         if (!bSuccess) //if not success, break loop
        {
             cout << "Cannot read a frame from video stream" << endl;
             break;
        }

	Mat imgHSV;

	cvtColor(imgOriginal, imgHSV, COLOR_BGR2HSV); //Convert the captured frame from BGR to HSV

	Mat imgThresholded;

	inRange(imgHSV, Scalar(iLowH, iLowS, iLowV), Scalar(iHighH, iHighS, iHighV), imgThresholded); //Threshold the image

	//morphological opening (remove small objects from the foreground)
	erode(imgThresholded, imgThresholded, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)) );
	dilate( imgThresholded, imgThresholded, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)) );

	//morphological closing (fill small holes in the foreground)
	dilate( imgThresholded, imgThresholded, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)) );
	erode(imgThresholded, imgThresholded, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)) );

	imshow("Thresholded Image", imgThresholded); //show the thresholded image
	imshow("Original", imgOriginal); //show the original image

    if (waitKey(30) == 27) //wait for 'esc' key press for 30ms. If 'esc' key is pressed, break loop
       {
            cout << "esc key is pressed by user" << endl;
            break;
       }
    }

   return 0;

}
////////////////////////////////////////////////////////////////////////////
<commit_msg>Added tracking<commit_after>//============================================================================
// Name        : ComputerVision.cpp
// Author      : Luke Hsiao, Clover Wu
// Version     :
// Copyright   : Copyright 2015 Team Vektor Krum
// Description : Hello World in C++, Ansi-style
//============================================================================

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

 int main( int argc, char** argv )
 {
    VideoCapture cap(0); //capture the video from webcam

    if ( !cap.isOpened() )  // if not success, exit program
    {
         cout << "Cannot open the web cam" << endl;
         return -1;
    }

    namedWindow("Control", CV_WINDOW_AUTOSIZE); //create a window called "Control"

  int iLowH = 170;
  int iHighH = 179;

  int iLowS = 150;
  int iHighS = 255;

  int iLowV = 60;
  int iHighV = 255;

  //Create trackbars in "Control" window
  createTrackbar("LowH", "Control", &iLowH, 179); //Hue (0 - 179)
  createTrackbar("HighH", "Control", &iHighH, 179);

  createTrackbar("LowS", "Control", &iLowS, 255); //Saturation (0 - 255)
  createTrackbar("HighS", "Control", &iHighS, 255);

  createTrackbar("LowV", "Control", &iLowV, 255);//Value (0 - 255)
  createTrackbar("HighV", "Control", &iHighV, 255);

  int iLastX = -1;
  int iLastY = -1;

  //Capture a temporary image from the camera
  Mat imgTmp;
  cap.read(imgTmp);

  //Create a black image with the size as the camera output
  Mat imgLines = Mat::zeros( imgTmp.size(), CV_8UC3 );;

  while (true)
  {
    Mat imgOriginal;

    bool bSuccess = cap.read(imgOriginal); // read a new frame from video

    if (!bSuccess) {//if not success, break loop
      cout << "Cannot read a frame from video stream" << endl;
      break;
    }

    Mat imgHSV;

    cvtColor(imgOriginal, imgHSV, COLOR_BGR2HSV); //Convert the captured frame from BGR to HSV

    Mat imgThresholded;

    inRange(imgHSV, Scalar(iLowH, iLowS, iLowV), Scalar(iHighH, iHighS, iHighV), imgThresholded); //Threshold the image

    //morphological opening (removes small objects from the foreground)
    erode(imgThresholded, imgThresholded, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)) );
    dilate( imgThresholded, imgThresholded, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)) );

    //morphological closing (removes small holes from the foreground)
    dilate( imgThresholded, imgThresholded, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)) );
    erode(imgThresholded, imgThresholded, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)) );

    //Calculate the moments of the thresholded image
    Moments oMoments = moments(imgThresholded);

    double dM01 = oMoments.m01;
    double dM10 = oMoments.m10;
    double dArea = oMoments.m00;

    // if the area <= 10000, I consider that the there are no object in the image and it's because of the noise, the area is not zero
    if (dArea > 10000) {
      //calculate the position of the ball
      int posX = dM10 / dArea;
      int posY = dM01 / dArea;

      if (iLastX >= 0 && iLastY >= 0 && posX >= 0 && posY >= 0) {
        //Draw a red line from the previous point to the current point
        line(imgLines, Point(posX, posY), Point(iLastX, iLastY), Scalar(0,0,255), 2);
      }

      iLastX = posX;
      iLastY = posY;
    }

    imshow("Thresholded Image", imgThresholded); //show the thresholded image

    imgOriginal = imgOriginal + imgLines;
    imshow("Original", imgOriginal); //show the original image

    if (waitKey(30) == 27) { //wait for 'esc' key press for 30ms. If 'esc' key is pressed, break loop
      cout << "esc key is pressed by user" << endl;
      break;
    }
  }
  return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
<|endoftext|>


<commit_before>
/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */

/*
 *  Main authors:
 *     Gleb Belov <gleb.belov@monash.edu>
 */

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef __MIP_GUROBI_WRAPPER_H__
#define __MIP_GUROBI_WRAPPER_H__

#include <minizinc/solvers/MIP/MIP_wrap.hh>
extern "C" {
  #include <gurobi_c.h>     // need GUROBI_HOME defined
}

class MIP_gurobi_wrapper : public MIP_wrapper {
    GRBenv        * env = 0;
    GRBmodel      * model = 0;
#ifdef HAS_GUROBI_PLUGIN
    void          * gurobi_dll;
#endif
  int             error;
    string          gurobi_buffer;   // [GRB_MESSAGEBUFSIZE];
    string          gurobi_status_buffer; // [GRB_MESSAGEBUFSIZE];

    vector<double> x;

  public:
    int (*dll_GRBaddconstr) (GRBmodel *model, int numnz, int *cind, double *cval,
                             char sense, double rhs, const char *constrname);

    int (*dll_GRBaddvars) (GRBmodel *model, int numvars, int numnz,
                           int *vbeg, int *vind, double *vval,
                           double *obj, double *lb, double *ub, char *vtype,
                           char **varnames);

    int (*dll_GRBcbcut) (void *cbdata, int cutlen, const int *cutind, const double *cutval,
                         char cutsense, double cutrhs);

    int (*dll_GRBcbget) (void *cbdata, int where, int what, void *resultP);

    int (*dll_GRBcblazy) (void *cbdata, int lazylen, const int *lazyind,
                          const double *lazyval, char lazysense, double lazyrhs);

    void (*dll_GRBfreeenv) (GRBenv *env);

    int (*dll_GRBfreemodel) (GRBmodel *model);

    int (*dll_GRBgetdblattr) (GRBmodel *model, const char *attrname, double *valueP);

    int (*dll_GRBgetdblattrarray) (GRBmodel *model, const char *attrname,
                                   int first, int len, double *values);

    GRBenv * (*dll_GRBgetenv) (GRBmodel *model);

    const char * (*dll_GRBgeterrormsg) (GRBenv *env);

    int (*dll_GRBgetintattr) (GRBmodel *model, const char *attrname, int *valueP);

    int (*dll_GRBloadenv) (GRBenv **envP, const char *logfilename);

    int (*dll_GRBnewmodel) (GRBenv *env, GRBmodel **modelP, const char *Pname, int numvars,
                            double *obj, double *lb, double *ub, char *vtype,
                            char **varnames);

    int (*dll_GRBoptimize) (GRBmodel *model);

    int (*dll_GRBreadparams) (GRBenv *env, const char *filename);

    int (*dll_GRBsetcallbackfunc) (GRBmodel *model,
                                   int (__stdcall *cb)(CB_ARGS),
                                   void  *usrdata);

    int (*dll_GRBsetdblparam) (GRBenv *env, const char *paramname, double value);

    int (*dll_GRBsetintparam) (GRBenv *env, const char *paramname, int value);

    int (*dll_GRBsetintattr) (GRBmodel *model, const char *attrname, int newvalue);

    int (*dll_GRBsetintattrlist) (GRBmodel *model, const char *attrname,
                    int len, int *ind, int *newvalues);

    int (*dll_GRBsetstrparam) (GRBenv *env, const char *paramname, const char *value);

    int (*dll_GRBupdatemodel) (GRBmodel *model);

    int (*dll_GRBwrite) (GRBmodel *model, const char *filename);

    int (*dll_GRBwriteparams) (GRBenv *env, const char *filename);

    int (*dll_GRBgetintparam) (GRBenv *env, const char *paramname, int *valueP);

  public:
    MIP_gurobi_wrapper() { openGUROBI(); }
    virtual ~MIP_gurobi_wrapper() { closeGUROBI(); }

    bool processOption(int& i, int argc, const char** argv);
    void printVersion(ostream& );
    void printHelp(ostream& );
//       Statistics& getStatistics() { return _statistics; }

//      IloConstraintArray *userCuts, *lazyConstraints;

    /// derived should overload and call the ancestor
//     virtual void cleanup();
    void openGUROBI();
    void closeGUROBI();

    /// actual adding new variables to the solver
    virtual void doAddVars(size_t n, double *obj, double *lb, double *ub,
      VarType *vt, string *names);

    /// adding a linear constraint
    virtual void addRow(int nnz, int *rmatind, double* rmatval,
                        LinConType sense, double rhs,
                        int mask = MaskConsType_Normal,
                        string rowName = "");
    int nRows=0;    // to count rows in order tp notice lazy constraints
    std::vector<int> nLazyIdx;
    std::vector<int> nLazyValue;
    /// adding an implication
//     virtual void addImpl() = 0;
    virtual void setObjSense(int s);   // +/-1 for max/min

    virtual double getInfBound() { return GRB_INFINITY; }

    virtual int getNCols() {
      dll_GRBupdatemodel(model);
      int cols; error = dll_GRBgetintattr(model, GRB_INT_ATTR_NUMVARS, &cols); return cols;
    }
    virtual int getNRows() {
      dll_GRBupdatemodel(model);
      int cols; error = dll_GRBgetintattr(model, GRB_INT_ATTR_NUMCONSTRS, &cols); return cols;
    }

//     void setObjUB(double ub) { objUB = ub; }
//     void addQPUniform(double c) { qpu = c; } // also sets problem type to MIQP unless c=0

    virtual void solve();

    /// OUTPUT:
    virtual const double* getValues() { return output.x; }
    virtual double getObjValue() { return output.objVal; }
    virtual double getBestBound() { return output.bestBound; }
    virtual double getCPUTime() { return output.dCPUTime; }

    virtual Status getStatus()  { return output.status; }
    virtual string getStatusName() { return output.statusName; }

     virtual int getNNodes() { return output.nNodes; }
     virtual int getNOpen() { return output.nOpenNodes; }

//     virtual int getNNodes() = 0;
//     virtual double getTime() = 0;

  protected:
    void wrap_assert(bool , string , bool fTerm=true);

    /// Need to consider the 100 status codes in GUROBI and change with every version? TODO
    Status convertStatus(int gurobiStatus);
};

#endif  // __MIP_GUROBI_WRAPPER_H__
<commit_msg>Add __stdcall annotations for msvc<commit_after>
/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */

/*
 *  Main authors:
 *     Gleb Belov <gleb.belov@monash.edu>
 */

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef __MIP_GUROBI_WRAPPER_H__
#define __MIP_GUROBI_WRAPPER_H__

#include <minizinc/solvers/MIP/MIP_wrap.hh>
extern "C" {
  #include <gurobi_c.h>     // need GUROBI_HOME defined
}

class MIP_gurobi_wrapper : public MIP_wrapper {
    GRBenv        * env = 0;
    GRBmodel      * model = 0;
#ifdef HAS_GUROBI_PLUGIN
    void          * gurobi_dll;
#endif
  int             error;
    string          gurobi_buffer;   // [GRB_MESSAGEBUFSIZE];
    string          gurobi_status_buffer; // [GRB_MESSAGEBUFSIZE];

    vector<double> x;

  public:
    int (__stdcall *dll_GRBaddconstr) (GRBmodel *model, int numnz, int *cind, double *cval,
                             char sense, double rhs, const char *constrname);

    int (__stdcall *dll_GRBaddvars) (GRBmodel *model, int numvars, int numnz,
                           int *vbeg, int *vind, double *vval,
                           double *obj, double *lb, double *ub, char *vtype,
                           char **varnames);

    int (__stdcall *dll_GRBcbcut) (void *cbdata, int cutlen, const int *cutind, const double *cutval,
                         char cutsense, double cutrhs);

    int (__stdcall *dll_GRBcbget) (void *cbdata, int where, int what, void *resultP);

    int (__stdcall *dll_GRBcblazy) (void *cbdata, int lazylen, const int *lazyind,
                          const double *lazyval, char lazysense, double lazyrhs);

    void (__stdcall *dll_GRBfreeenv) (GRBenv *env);

    int (__stdcall *dll_GRBfreemodel) (GRBmodel *model);

    int (__stdcall *dll_GRBgetdblattr) (GRBmodel *model, const char *attrname, double *valueP);

    int (__stdcall *dll_GRBgetdblattrarray) (GRBmodel *model, const char *attrname,
                                   int first, int len, double *values);

    GRBenv * (__stdcall *dll_GRBgetenv) (GRBmodel *model);

    const char * (__stdcall *dll_GRBgeterrormsg) (GRBenv *env);

    int (__stdcall *dll_GRBgetintattr) (GRBmodel *model, const char *attrname, int *valueP);

    int (__stdcall *dll_GRBloadenv) (GRBenv **envP, const char *logfilename);

    int (__stdcall *dll_GRBnewmodel) (GRBenv *env, GRBmodel **modelP, const char *Pname, int numvars,
                            double *obj, double *lb, double *ub, char *vtype,
                            char **varnames);

    int (__stdcall *dll_GRBoptimize) (GRBmodel *model);

    int (__stdcall *dll_GRBreadparams) (GRBenv *env, const char *filename);

    int (__stdcall *dll_GRBsetcallbackfunc) (GRBmodel *model,
                                   int (__stdcall *cb)(CB_ARGS),
                                   void  *usrdata);

    int (__stdcall *dll_GRBsetdblparam) (GRBenv *env, const char *paramname, double value);

    int (__stdcall *dll_GRBsetintparam) (GRBenv *env, const char *paramname, int value);

    int (__stdcall *dll_GRBsetintattr) (GRBmodel *model, const char *attrname, int newvalue);

    int (__stdcall *dll_GRBsetintattrlist) (GRBmodel *model, const char *attrname,
                    int len, int *ind, int *newvalues);

    int (__stdcall *dll_GRBsetstrparam) (GRBenv *env, const char *paramname, const char *value);

    int (__stdcall *dll_GRBupdatemodel) (GRBmodel *model);

    int (__stdcall *dll_GRBwrite) (GRBmodel *model, const char *filename);

    int (__stdcall *dll_GRBwriteparams) (GRBenv *env, const char *filename);

    int (__stdcall *dll_GRBgetintparam) (GRBenv *env, const char *paramname, int *valueP);

  public:
    MIP_gurobi_wrapper() { openGUROBI(); }
    virtual ~MIP_gurobi_wrapper() { closeGUROBI(); }

    bool processOption(int& i, int argc, const char** argv);
    void printVersion(ostream& );
    void printHelp(ostream& );
//       Statistics& getStatistics() { return _statistics; }

//      IloConstraintArray *userCuts, *lazyConstraints;

    /// derived should overload and call the ancestor
//     virtual void cleanup();
    void openGUROBI();
    void closeGUROBI();

    /// actual adding new variables to the solver
    virtual void doAddVars(size_t n, double *obj, double *lb, double *ub,
      VarType *vt, string *names);

    /// adding a linear constraint
    virtual void addRow(int nnz, int *rmatind, double* rmatval,
                        LinConType sense, double rhs,
                        int mask = MaskConsType_Normal,
                        string rowName = "");
    int nRows=0;    // to count rows in order tp notice lazy constraints
    std::vector<int> nLazyIdx;
    std::vector<int> nLazyValue;
    /// adding an implication
//     virtual void addImpl() = 0;
    virtual void setObjSense(int s);   // +/-1 for max/min

    virtual double getInfBound() { return GRB_INFINITY; }

    virtual int getNCols() {
      dll_GRBupdatemodel(model);
      int cols; error = dll_GRBgetintattr(model, GRB_INT_ATTR_NUMVARS, &cols); return cols;
    }
    virtual int getNRows() {
      dll_GRBupdatemodel(model);
      int cols; error = dll_GRBgetintattr(model, GRB_INT_ATTR_NUMCONSTRS, &cols); return cols;
    }

//     void setObjUB(double ub) { objUB = ub; }
//     void addQPUniform(double c) { qpu = c; } // also sets problem type to MIQP unless c=0

    virtual void solve();

    /// OUTPUT:
    virtual const double* getValues() { return output.x; }
    virtual double getObjValue() { return output.objVal; }
    virtual double getBestBound() { return output.bestBound; }
    virtual double getCPUTime() { return output.dCPUTime; }

    virtual Status getStatus()  { return output.status; }
    virtual string getStatusName() { return output.statusName; }

     virtual int getNNodes() { return output.nNodes; }
     virtual int getNOpen() { return output.nOpenNodes; }

//     virtual int getNNodes() = 0;
//     virtual double getTime() = 0;

  protected:
    void wrap_assert(bool , string , bool fTerm=true);

    /// Need to consider the 100 status codes in GUROBI and change with every version? TODO
    Status convertStatus(int gurobiStatus);
};

#endif  // __MIP_GUROBI_WRAPPER_H__
<|endoftext|>


<commit_before>#pragma once

#include <vector>
#include <tudocomp/compressors/lz78/LZ78Trie.hpp>
#include <tudocomp/Algorithm.hpp>

#include "cedar.hpp"

namespace tdc {
namespace lz78 {

using cedar_factorid_t = lz78::factorid_t;
using cedar_t = cedar::da<cedar_factorid_t>;

const cedar_factorid_t CEDAR_NO_VALUE = static_cast<cedar_factorid_t>(cedar_t::error_code::CEDAR_NO_VALUE);
const cedar_factorid_t CEDAR_NO_PATH = static_cast<cedar_factorid_t>(cedar_t::error_code::CEDAR_NO_PATH);

struct CedarSearchPos {
    size_t from;
};

template<>
class TrieNode<CedarSearchPos> {
    factorid_t m_id;
    CedarSearchPos m_search_pos;
public:
    TrieNode(const factorid_t& id, const CedarSearchPos& search_pos):
        m_id(id),
        m_search_pos(search_pos) {
            //DCHECK(id != CEDAR_NO_VALUE && id != CEDAR_NO_PATH);
        }
    TrieNode(): TrieNode(0, CedarSearchPos { 0 }) {}

    inline const factorid_t& id() const { return m_id; }
    inline const CedarSearchPos& search_pos() const { return m_search_pos; }
};

class LzwRootSearchPosMap {
    std::array<CedarSearchPos, 256> m_array;
public:
    inline CedarSearchPos get(uliteral_t c) {
        DCHECK(c < m_array.size());
        return m_array[c];
    }
    inline void set(uliteral_t c, CedarSearchPos v) {
        DCHECK(c < m_array.size());
        m_array[c] = v;
    }
};

const uint8_t NULL_ESCAPE_ESCAPE_BYTE = 255;
const uint8_t NULL_ESCAPE_REPLACEMENT_BYTE = 254;

const cedar_factorid_t HIDDEN_ESCAPE_ID = -3; // NOTE: May not be -1 or -2

class CedarTrie: public Algorithm, public LZ78Trie<CedarSearchPos> {
    // unique_ptr only needed for reassignment
    std::unique_ptr<cedar_t> m_trie;
    cedar_factorid_t m_ids = 0;
    LzwRootSearchPosMap m_roots;

    inline node_t _find_or_insert(const node_t& parent, uliteral_t c, bool incr_id) {
        auto search_pos = parent.search_pos();

        auto letter = (const char*) &c;
        auto& from = search_pos.from;
        cedar_factorid_t searchResult;

        {
            size_t pos = 0;
            searchResult = m_trie->traverse(letter, from, pos, 1);
        }

        node_t r;

        if(searchResult != CEDAR_NO_VALUE && searchResult != CEDAR_NO_PATH) {
            r = node_t {
                searchResult - 1,
                search_pos,
            };
        } else {
            {
                size_t pos = 0;
                if (incr_id) {
                    m_trie->update(letter, from, pos, 1, ++m_ids);
                } else {
                    m_trie->update(letter, from, pos, 1, HIDDEN_ESCAPE_ID);
                }
            }
            r = node_t {
                lz78::undef_id,
                search_pos,
            };
        }

        return r;
    }

    inline void _print(size_t from, size_t ind) {
        cedar_t& t = *m_trie;

        bool prev_empty = false;
        size_t prev_empty_min = 1;
        size_t prev_empty_max = 1;

        auto print_prev_empty = [&]() {
            if (prev_empty) {
                DLOG(INFO)
                    << std::setfill(' ')
                    << std::setw(ind)
                    << ""
                    << "["
                    << std::setfill('0')
                    << std::setw(3)
                    << prev_empty_min
                    << "]: "
                    << "-"
                    << "\n";
                if (prev_empty_min != prev_empty_max) {
                    DLOG(INFO)
                        << std::setfill(' ')
                        << std::setw(ind)
                        << ""
                        << "[...]\n";
                    DLOG(INFO)
                        << std::setfill(' ')
                        << std::setw(ind)
                        << ""
                        << "["
                        << std::setfill('0')
                        << std::setw(3)
                        << prev_empty_max
                        << "]: "
                        << "-"
                        << "\n";
                }
                prev_empty = false;
            }
        };

        for (size_t i = 1; i < 256; i++) {
            auto child_from = from;
            const char c = uint8_t(i);
            size_t pos = 0;
            auto r = t.traverse(&c, child_from, pos, 1);
            if (r != CEDAR_NO_PATH && r != CEDAR_NO_VALUE) {
                print_prev_empty();
                prev_empty_min = i + 1;
                DLOG(INFO)
                    << std::setfill(' ')
                    << std::setw(ind)
                    << ""
                    << "["
                    << std::setfill('0')
                    << std::setw(3)
                    << i
                    << std::setfill(' ')
                    << "]: "
                    << r
                    << "\n";
                _print(child_from, ind + 4);
            } else {
                prev_empty = true;
                prev_empty_max = i;
            }
        }
        print_prev_empty();
    }

    inline void print() {
        DLOG(INFO) << "\n";
        _print(0, 0);
        DLOG(INFO) << "\n";
    }

public:
    inline static Meta meta() {
        Meta m("lz78trie", "cedar", "Lempel-Ziv 78 Cedar Trie");
        return m;
    }

    CedarTrie(Env&& env, const size_t n, const size_t& remaining_characters, factorid_t = 0)
        : Algorithm(std::move(env))
		, LZ78Trie(n, remaining_characters)
        , m_trie(std::make_unique<cedar_t>()) {}

    inline node_t add_rootnode(const uliteral_t c) override final {
        cedar_factorid_t ids = c;
        DCHECK(m_ids == ids);
        m_ids++;

        CedarSearchPos search_pos;

        if (c != 0 && c != NULL_ESCAPE_ESCAPE_BYTE) {
            const char* letter = (const char*) &c;
            size_t from = 0;
            size_t pos = 0;
            m_trie->update(letter, from, pos, 1, ids);
            DCHECK(pos == 1);
            search_pos = CedarSearchPos{ from };
        } else {
            const char* letter;
            size_t from = 0;
            size_t pos;

            pos = 0;
            letter = (const char*) &NULL_ESCAPE_ESCAPE_BYTE;
            auto res = m_trie->traverse(letter, from, pos, 1);

            if (res == CEDAR_NO_PATH || res == CEDAR_NO_VALUE) {
                DCHECK(pos == 0);
                m_trie->update(letter, from, pos, 1, cedar_factorid_t(HIDDEN_ESCAPE_ID));
                DCHECK(pos == 1);
            }

            pos = 0;
            char c2 = c;
            if (c == 0) c2 = NULL_ESCAPE_REPLACEMENT_BYTE;
            letter = (const char*) &c2;
            m_trie->update(letter, from, pos, 1, ids);
            DCHECK(pos == 1);

            search_pos = CedarSearchPos{ from };
        }
        auto r = node_t(ids, search_pos);
        m_roots.set(c, search_pos);
        /*
        DLOG(INFO) << "add rootnode "
            << "char: " << int(c)
            << ", factor id: "
            << r.id() << ", from: "
            << r.search_pos().from;
        print();
        */
        return r;
    }

    inline node_t get_rootnode(uliteral_t c) override final {
        return node_t(c, m_roots.get(c));
    }

    inline void clear() override final {
        // TODO: cedar seems to have a clear() method, but also
        // seems to have bugs in its implementation
        m_trie = std::make_unique<cedar_t>();
        m_ids = 0;
        m_roots = LzwRootSearchPosMap();
    }

    inline node_t find_or_insert(const node_t& parent, uliteral_t c) override final {
        node_t r;
        /*
        DLOG(INFO) << "find or insert "
            << "char: " << int(c)
            << ", factor id: "
            << parent.id() << ", from: "
            << parent.search_pos().from;
        */
        if (c == 0) {
            auto r1 = _find_or_insert(parent, NULL_ESCAPE_ESCAPE_BYTE, false);
            auto r2 = _find_or_insert(r1, NULL_ESCAPE_REPLACEMENT_BYTE, true);
            r = r2;
        } else if (c == NULL_ESCAPE_ESCAPE_BYTE) {
            auto r1 = _find_or_insert(parent, NULL_ESCAPE_ESCAPE_BYTE, false);
            auto r2 = _find_or_insert(r1, NULL_ESCAPE_ESCAPE_BYTE, true);
            r = r2;
        } else {
            r = _find_or_insert(parent, c, true);
        }
        //print();
        return r;
    }

    inline factorid_t size() const override final {
        return m_ids;
    }
};

}} //ns

<commit_msg>adjust CedarTrie to non-virtual<commit_after>#pragma once

#include <vector>
#include <tudocomp/compressors/lz78/LZ78Trie.hpp>
#include <tudocomp/Algorithm.hpp>

#include "cedar.hpp"

namespace tdc {
namespace lz78 {

using cedar_factorid_t = lz78::factorid_t;
using cedar_t = cedar::da<cedar_factorid_t>;

const cedar_factorid_t CEDAR_NO_VALUE = static_cast<cedar_factorid_t>(cedar_t::error_code::CEDAR_NO_VALUE);
const cedar_factorid_t CEDAR_NO_PATH = static_cast<cedar_factorid_t>(cedar_t::error_code::CEDAR_NO_PATH);

struct CedarSearchPos {
    size_t from;
};

template<>
class TrieNode<CedarSearchPos> {
    factorid_t m_id;
    CedarSearchPos m_search_pos;
public:
    TrieNode(const factorid_t& id, const CedarSearchPos& search_pos):
        m_id(id),
        m_search_pos(search_pos) {
            //DCHECK(id != CEDAR_NO_VALUE && id != CEDAR_NO_PATH);
        }
    TrieNode(): TrieNode(0, CedarSearchPos { 0 }) {}

    inline const factorid_t& id() const { return m_id; }
    inline const CedarSearchPos& search_pos() const { return m_search_pos; }
};

class LzwRootSearchPosMap {
    std::array<CedarSearchPos, 256> m_array;
public:
    inline CedarSearchPos get(uliteral_t c) {
        DCHECK(c < m_array.size());
        return m_array[c];
    }
    inline void set(uliteral_t c, CedarSearchPos v) {
        DCHECK(c < m_array.size());
        m_array[c] = v;
    }
};

const uint8_t NULL_ESCAPE_ESCAPE_BYTE = 255;
const uint8_t NULL_ESCAPE_REPLACEMENT_BYTE = 254;

const cedar_factorid_t HIDDEN_ESCAPE_ID = -3; // NOTE: May not be -1 or -2

class CedarTrie: public Algorithm, public LZ78Trie<CedarSearchPos> {
    // unique_ptr only needed for reassignment
    std::unique_ptr<cedar_t> m_trie;
    cedar_factorid_t m_ids = 0;
    LzwRootSearchPosMap m_roots;

    inline node_t _find_or_insert(const node_t& parent, uliteral_t c, bool incr_id) {
        auto search_pos = parent.search_pos();

        auto letter = (const char*) &c;
        auto& from = search_pos.from;
        cedar_factorid_t searchResult;

        {
            size_t pos = 0;
            searchResult = m_trie->traverse(letter, from, pos, 1);
        }

        node_t r;

        if(searchResult != CEDAR_NO_VALUE && searchResult != CEDAR_NO_PATH) {
            r = node_t {
                searchResult - 1,
                search_pos,
            };
        } else {
            {
                size_t pos = 0;
                if (incr_id) {
                    m_trie->update(letter, from, pos, 1, ++m_ids);
                } else {
                    m_trie->update(letter, from, pos, 1, HIDDEN_ESCAPE_ID);
                }
            }
            r = node_t {
                lz78::undef_id,
                search_pos,
            };
        }

        return r;
    }

    inline void _print(size_t from, size_t ind) {
        cedar_t& t = *m_trie;

        bool prev_empty = false;
        size_t prev_empty_min = 1;
        size_t prev_empty_max = 1;

        auto print_prev_empty = [&]() {
            if (prev_empty) {
                DLOG(INFO)
                    << std::setfill(' ')
                    << std::setw(ind)
                    << ""
                    << "["
                    << std::setfill('0')
                    << std::setw(3)
                    << prev_empty_min
                    << "]: "
                    << "-"
                    << "\n";
                if (prev_empty_min != prev_empty_max) {
                    DLOG(INFO)
                        << std::setfill(' ')
                        << std::setw(ind)
                        << ""
                        << "[...]\n";
                    DLOG(INFO)
                        << std::setfill(' ')
                        << std::setw(ind)
                        << ""
                        << "["
                        << std::setfill('0')
                        << std::setw(3)
                        << prev_empty_max
                        << "]: "
                        << "-"
                        << "\n";
                }
                prev_empty = false;
            }
        };

        for (size_t i = 1; i < 256; i++) {
            auto child_from = from;
            const char c = uint8_t(i);
            size_t pos = 0;
            auto r = t.traverse(&c, child_from, pos, 1);
            if (r != CEDAR_NO_PATH && r != CEDAR_NO_VALUE) {
                print_prev_empty();
                prev_empty_min = i + 1;
                DLOG(INFO)
                    << std::setfill(' ')
                    << std::setw(ind)
                    << ""
                    << "["
                    << std::setfill('0')
                    << std::setw(3)
                    << i
                    << std::setfill(' ')
                    << "]: "
                    << r
                    << "\n";
                _print(child_from, ind + 4);
            } else {
                prev_empty = true;
                prev_empty_max = i;
            }
        }
        print_prev_empty();
    }

    inline void print() {
        DLOG(INFO) << "\n";
        _print(0, 0);
        DLOG(INFO) << "\n";
    }

public:
    inline static Meta meta() {
        Meta m("lz78trie", "cedar", "Lempel-Ziv 78 Cedar Trie");
        return m;
    }

    CedarTrie(Env&& env, const size_t n, const size_t& remaining_characters, factorid_t = 0)
        : Algorithm(std::move(env))
		, LZ78Trie(n, remaining_characters)
        , m_trie(std::make_unique<cedar_t>()) {}

    inline node_t add_rootnode(const uliteral_t c) {
        cedar_factorid_t ids = c;
        DCHECK(m_ids == ids);
        m_ids++;

        CedarSearchPos search_pos;

        if (c != 0 && c != NULL_ESCAPE_ESCAPE_BYTE) {
            const char* letter = (const char*) &c;
            size_t from = 0;
            size_t pos = 0;
            m_trie->update(letter, from, pos, 1, ids);
            DCHECK(pos == 1);
            search_pos = CedarSearchPos{ from };
        } else {
            const char* letter;
            size_t from = 0;
            size_t pos;

            pos = 0;
            letter = (const char*) &NULL_ESCAPE_ESCAPE_BYTE;
            auto res = m_trie->traverse(letter, from, pos, 1);

            if (res == CEDAR_NO_PATH || res == CEDAR_NO_VALUE) {
                DCHECK(pos == 0);
                m_trie->update(letter, from, pos, 1, cedar_factorid_t(HIDDEN_ESCAPE_ID));
                DCHECK(pos == 1);
            }

            pos = 0;
            char c2 = c;
            if (c == 0) c2 = NULL_ESCAPE_REPLACEMENT_BYTE;
            letter = (const char*) &c2;
            m_trie->update(letter, from, pos, 1, ids);
            DCHECK(pos == 1);

            search_pos = CedarSearchPos{ from };
        }
        auto r = node_t(ids, search_pos);
        m_roots.set(c, search_pos);
        /*
        DLOG(INFO) << "add rootnode "
            << "char: " << int(c)
            << ", factor id: "
            << r.id() << ", from: "
            << r.search_pos().from;
        print();
        */
        return r;
    }

    inline node_t get_rootnode(uliteral_t c) {
        return node_t(c, m_roots.get(c));
    }

    inline void clear() {
        // TODO: cedar seems to have a clear() method, but also
        // seems to have bugs in its implementation
        m_trie = std::make_unique<cedar_t>();
        m_ids = 0;
        m_roots = LzwRootSearchPosMap();
    }

    inline node_t find_or_insert(const node_t& parent, uliteral_t c) {
        node_t r;
        /*
        DLOG(INFO) << "find or insert "
            << "char: " << int(c)
            << ", factor id: "
            << parent.id() << ", from: "
            << parent.search_pos().from;
        */
        if (c == 0) {
            auto r1 = _find_or_insert(parent, NULL_ESCAPE_ESCAPE_BYTE, false);
            auto r2 = _find_or_insert(r1, NULL_ESCAPE_REPLACEMENT_BYTE, true);
            r = r2;
        } else if (c == NULL_ESCAPE_ESCAPE_BYTE) {
            auto r1 = _find_or_insert(parent, NULL_ESCAPE_ESCAPE_BYTE, false);
            auto r2 = _find_or_insert(r1, NULL_ESCAPE_ESCAPE_BYTE, true);
            r = r2;
        } else {
            r = _find_or_insert(parent, c, true);
        }
        //print();
        return r;
    }

    inline factorid_t size() const {
        return m_ids;
    }
};

}} //ns

<|endoftext|>


<commit_before>/**
 * @file llcrashloggerlinux.cpp
 * @brief Linux crash logger implementation
 *
 * $LicenseInfo:firstyear=2003&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include "llcrashloggerlinux.h"

#include <iostream>

#include "linden_common.h"

#include "indra_constants.h"	// CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
#include "llerror.h"
#include "llfile.h"
#include "lltimer.h"
#include "llstring.h"
#include "lldir.h"
#include "llsdserialize.h"

#if LL_GTK
# include "gtk/gtk.h"
#endif // LL_GTK

#define MAX_LOADSTRING 100

// These need to be localized.
static const char dialog_text[] =
"Second Life appears to have crashed or frozen last time it ran.\n"
"This crash reporter collects information about your computer's hardware, operating system, and some Second Life logs, all of which are used for debugging purposes only.\n"
"\n"
"Send crash report?";

static const char dialog_title[] =
"Second Life Crash Logger";

#if LL_GTK
static void response_callback (GtkDialog *dialog,
			       gint       arg1,
			       gpointer   user_data)
{
	gint *response = (gint*)user_data;
	*response = arg1;
	gtk_widget_destroy(GTK_WIDGET(dialog));
	gtk_main_quit();
}
#endif // LL_GTK

static BOOL do_ask_dialog(void)
{
#if LL_GTK
	gtk_disable_setlocale();
	if (!gtk_init_check(NULL, NULL)) {
		llinfos << "Could not initialize GTK for 'ask to send crash report' dialog; not sending report." << llendl;
		return FALSE;
	}

	GtkWidget *win = NULL;
	GtkDialogFlags flags = GTK_DIALOG_MODAL;
	GtkMessageType messagetype = GTK_MESSAGE_QUESTION;
	GtkButtonsType buttons = GTK_BUTTONS_YES_NO;
	gint response = GTK_RESPONSE_NONE;

	win = gtk_message_dialog_new(NULL,
				     flags, messagetype, buttons,
				     "%s", dialog_text);
	gtk_window_set_type_hint(GTK_WINDOW(win),
				 GDK_WINDOW_TYPE_HINT_DIALOG);
	gtk_window_set_title(GTK_WINDOW(win), dialog_title);
	g_signal_connect (win,
			  "response",
			  G_CALLBACK (response_callback),
			  &response);
	gtk_widget_show_all (win);
	gtk_main();

	return (GTK_RESPONSE_OK == response ||
		GTK_RESPONSE_YES == response ||
		GTK_RESPONSE_APPLY == response);
#else
	return FALSE;
#endif // LL_GTK
}

LLCrashLoggerLinux::LLCrashLoggerLinux(void)
{
}

LLCrashLoggerLinux::~LLCrashLoggerLinux(void)
{
}

void LLCrashLoggerLinux::gatherPlatformSpecificFiles()
{
}

bool LLCrashLoggerLinux::mainLoop()
{
	bool send_logs = true;
	if(CRASH_BEHAVIOR_ASK == getCrashBehavior())
	{
		send_logs = do_ask_dialog();
	}
	else if(CRASH_BEHAVIOR_NEVER_SEND == getCrashBehavior())
	{
		send_logs = false;
	}

	if(send_logs)
	{
		sendCrashLogs();
	}
	return true;
}

bool LLCrashLoggerLinux::cleanup()
{
	commonCleanup();
	return true;
}

void LLCrashLoggerLinux::updateApplication(const std::string& message)
{
	LLCrashLogger::updateApplication(message);
}
<commit_msg>FIRE-901 / Crashreporting: Change SecondLife to Firestorm / Add privacy policy url to linux crash logger dialog.<commit_after>/**
 * @file llcrashloggerlinux.cpp
 * @brief Linux crash logger implementation
 *
 * $LicenseInfo:firstyear=2003&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include "llcrashloggerlinux.h"

#include <iostream>

#include "linden_common.h"

#include "indra_constants.h"	// CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
#include "llerror.h"
#include "llfile.h"
#include "lltimer.h"
#include "llstring.h"
#include "lldir.h"
#include "llsdserialize.h"

#if LL_GTK
# include "gtk/gtk.h"
#endif // LL_GTK

#define MAX_LOADSTRING 100

// <FS:ND> Fire-901 / Crashreporting: Brand for FS, add URL to privacy policy

// These need to be localized.
// static const char dialog_text[] =
// "Second Life appears to have crashed or frozen last time it ran.\n"
// "This crash reporter collects information about your computer's hardware, operating system, and some Second Life logs, all of which are used for debugging purposes only.\n"
// "\n"
// "Send crash report?";
//
// static const char dialog_title[] =
// "Second Life Crash Logger";

static const char dialog_text[] =
"Firestorm appears to have crashed or frozen last time it ran.\n"
"This crash reporter collects information about your computer's hardware, operating system which are used for debugging purposes only. SecondLife logs are not collected.\n"
"This report will be send to phoenixviewer.com. Review our privacy policy at http://www.phoenixviewer.com/privacy.php for more information.\n"
"\n"
"Send crash report?";

static const char dialog_title[] =
"Firestorm Crash Logger";

// </FS:ND>

#if LL_GTK
static void response_callback (GtkDialog *dialog,
			       gint       arg1,
			       gpointer   user_data)
{
	gint *response = (gint*)user_data;
	*response = arg1;
	gtk_widget_destroy(GTK_WIDGET(dialog));
	gtk_main_quit();
}
#endif // LL_GTK

static BOOL do_ask_dialog(void)
{
#if LL_GTK
	gtk_disable_setlocale();
	if (!gtk_init_check(NULL, NULL)) {
		llinfos << "Could not initialize GTK for 'ask to send crash report' dialog; not sending report." << llendl;
		return FALSE;
	}

	GtkWidget *win = NULL;
	GtkDialogFlags flags = GTK_DIALOG_MODAL;
	GtkMessageType messagetype = GTK_MESSAGE_QUESTION;
	GtkButtonsType buttons = GTK_BUTTONS_YES_NO;
	gint response = GTK_RESPONSE_NONE;

	win = gtk_message_dialog_new(NULL,
				     flags, messagetype, buttons,
				     "%s", dialog_text);
	gtk_window_set_type_hint(GTK_WINDOW(win),
				 GDK_WINDOW_TYPE_HINT_DIALOG);
	gtk_window_set_title(GTK_WINDOW(win), dialog_title);
	g_signal_connect (win,
			  "response",
			  G_CALLBACK (response_callback),
			  &response);
	gtk_widget_show_all (win);
	gtk_main();

	return (GTK_RESPONSE_OK == response ||
		GTK_RESPONSE_YES == response ||
		GTK_RESPONSE_APPLY == response);
#else
	return FALSE;
#endif // LL_GTK
}

LLCrashLoggerLinux::LLCrashLoggerLinux(void)
{
}

LLCrashLoggerLinux::~LLCrashLoggerLinux(void)
{
}

void LLCrashLoggerLinux::gatherPlatformSpecificFiles()
{
}

bool LLCrashLoggerLinux::mainLoop()
{
	bool send_logs = true;
	if(CRASH_BEHAVIOR_ASK == getCrashBehavior())
	{
		send_logs = do_ask_dialog();
	}
	else if(CRASH_BEHAVIOR_NEVER_SEND == getCrashBehavior())
	{
		send_logs = false;
	}

	if(send_logs)
	{
		sendCrashLogs();
	}
	return true;
}

bool LLCrashLoggerLinux::cleanup()
{
	commonCleanup();
	return true;
}

void LLCrashLoggerLinux::updateApplication(const std::string& message)
{
	LLCrashLogger::updateApplication(message);
}
<|endoftext|>