solarpowerlog trunk
/home/tobi/workspace/solarpowerlog/src/Inverters/SputnikEngineering/CInverterSputnikSSeries.cpp
Go to the documentation of this file.
00001 /* ----------------------------------------------------------------------------
00002  solarpowerlog
00003  Copyright (C) 2009  Tobias Frost
00004 
00005  This file is part of solarpowerlog.
00006 
00007  Solarpowerlog is free software; However, it is dual-licenced
00008  as described in the file "COPYING".
00009 
00010  For this file (CInverterSputnikSSeries.cpp), the license terms are:
00011 
00012  You can redistribute it and/or modify it under the terms of the GNU
00013  General Public License as published by the Free Software Foundation; either
00014  version 3 of the License, or (at your option) any later version.
00015 
00016  This program is distributed in the hope that it will be useful, but
00017  WITHOUT ANY WARRANTY; without even the implied warranty of
00018  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019  Lesser General Public License for more details.
00020 
00021  You should have received a copy of the GNU Library General Public
00022  License along with this proramm; if not, see
00023  <http://www.gnu.org/licenses/>.
00024  ----------------------------------------------------------------------------
00025  */
00026 
00027 // HOW TO HANLDE MULTI-PHASE UNITS
00028 // not implemented, but some ideas:
00029 // -- for all parameters, which are dependend on the phase,
00030 //    create extra parameters which the appendix _L[1-3]
00031 // -- create a filter, a "multi-phase-data-splitter", and connect it
00032 //    to the inverter.
00033 // -- it will search all capabilities for the phase variants and, if found,
00034 //     it will locally "rename" the capability for the following filters and displays.
00035 // --> The phases will be transformed to new data source, like a virtual inverter.
00036 
00043 #ifdef HAVE_CONFIG_H
00044 #include "config.h"
00045 #endif
00046 
00047 #ifdef HAVE_INV_SPUTNIK
00048 
00049 #include "configuration/Registry.h"
00050 #include "configuration/CConfigHelper.h"
00051 
00052 #include "CInverterSputnikSSeries.h"
00053 
00054 #include <libconfig.h++>
00055 #include <iostream>
00056 #include <sstream>
00057 #include <cstring>
00058 
00059 #include "patterns/ICommand.h"
00060 
00061 #include "interfaces/CWorkScheduler.h"
00062 
00063 #include "Inverters/Capabilites.h"
00064 #include "patterns/CValue.h"
00065 
00066 #include "configuration/ILogger.h"
00067 
00068 #include <cstring>
00069 
00070 using namespace libconfig;
00071 
00072 static struct
00073 {
00074      unsigned int typ;
00075      const char *description;
00076 }
00077           model_lookup[] = {
00078                     { 2001, "SolarMax 2000 E" },
00079                     { 3001, "SolarMax 3000 E" },
00080                     { 4000, "SolarMax 4000 E" },
00081                     { 6000, "SolarMax 6000 E" },
00082                     { 2010, "SolarMax 2000 C" },
00083                     { 3010, "SolarMax 3000 C" },
00084                     { 4010, "SolarMax 4000 C" },
00085                     { 4200, "SolarMax 4200 C" },
00086                     { 6010, "SolarMax 6000 C" },
00087                     { 20010, "SolarMax 2000 S" },
00088                     { 20020, "SolarMax 3000 S" },
00089                     { 20030, "SolarMax 4200 S" },
00090                     { 20040, "SolarMax 6000 S" },
00091                     { 20, "SolarMax 20 C" },
00092                     { 25, "SolarMax 25 C" },
00093                     { 30, "SolarMax 30 C" },
00094                     { 35, "SolarMax 35 C" },
00095                     { 50, "SolarMax 50 C" },
00096                     { 80, "SolarMax 80 C" },
00097                     { 100, "SolarMax 100 C" },
00098                     { 300, "SolarMax 300 C" },
00099                     {
00100                               -1,
00101                               "UKNOWN MODEL. PLEASE FILE A BUG WITH THE REPORTED ID, ALONG WITH ALL INFOS YOU HAVE" } };
00102 
00103 static struct
00104 {
00105      unsigned int code;
00106      enum InverterStatusCodes status;
00107      const char *description;
00108 }
00109           statuscodes[] = {
00110                     { 20002, NOT_FEEDING_OK, "Solar radiation too low" },
00111                     { 20003, NOT_FEEDING_OK, "Inverter Starting up" },
00112                     { 20004, FEEDING_MPP, "Feeding on MPP" },
00113 
00114                     { 20006, FEEDING_MAXPOWER, "Feeding. Inverter at power limit" },
00115                     { 20008, FEEDING, "Feeding" },
00116 
00117                     { 20115, NOT_FEEDING_EXTEVENT, "Off-grid" },
00118                     { 20116, NOT_FEEDING_EXTEVENT, "Grid Frequency too high" },
00119                     { 20117, NOT_FEEDING_EXTEVENT, "Grid Frequency too low" },
00120 
00121                     {
00122                               -1,
00123                               STATUS_UNAVAILABLE,
00124                               "Unknown Statuscode -- PLEASE FILE A BUG WITH AS MUCH INFOS AS YOU CAN FIND OUT -- BEST, READ THE DISPLAY OF THE INVERTER." }, };
00125 
00126 using namespace std;
00127 
00128 CInverterSputnikSSeries::CInverterSputnikSSeries(const string &name,
00129           const string & configurationpath) :
00130      IInverterBase::IInverterBase(name, configurationpath, "inverter")
00131 {
00132 
00133      swversion = swbuild = 0;
00134      ownadr = 0xfb;
00135      commadr = 0x01;
00136      laststatuscode = (unsigned int) -1;
00137      errcnt_ = 0;
00138 
00139      // Add the capabilites that this inverter has
00140      // Note: The "must-have" ones CAPA_CAPAS_REMOVEALL and CAPA_CAPAS_UPDATED are already instanciated by the base class constructor.
00141      // Note2: You also can add capabilites as soon you know them (runtime detection)
00142 
00143      string s;
00144      IValue *v;
00145      CCapability *c;
00146      s = CAPA_INVERTER_MANUFACTOR_NAME;
00147      v = IValue::Factory(CAPA_INVERTER_MANUFACTOR_TYPE);
00148      ((CValue<string>*) v)->Set("Sputnik Engineering");
00149      c = new CCapability(s, v, this);
00150      AddCapability(s, c);
00151 
00152      // Add the request to initialize as soon as the system is up.
00153      ICommand *cmd = new ICommand(CMD_INIT, this);
00154      Registry::GetMainScheduler()->ScheduleWork(cmd);
00155 
00156      CConfigHelper cfghlp(configurationpath);
00157      float interval;
00158      cfghlp.GetConfig("queryinterval", interval, 5.0f);
00159 
00160      // Query settings needed and default all optional settings.
00161      cfghlp.GetConfig("ownadr", ownadr, 0xFBu);
00162      cfghlp.GetConfig("commadr", commadr, 0x01u);
00163 
00164      s = CAPA_INVERTER_QUERYINTERVAL;
00165      v = IValue::Factory(CAPA_INVERTER_QUERYINTERVAL_TYPE);
00166      ((CValue<float>*) v)->Set(interval);
00167      c = new CCapability(s, v, this);
00168      AddCapability(s, c);
00169 
00170      s = CAPA_INVERTER_CONFIGNAME;
00171      v = IValue::Factory(CAPA_INVERTER_CONFIGNAME_TYPE);
00172      ((CValue<std::string>*) v)->Set(name);
00173      c = new CCapability(s, v, this);
00174      AddCapability(s, c);
00175 
00176      LOGDEBUG(logger,"Inverter configuration:");
00177      LOGDEBUG(logger,"class CInverterSputnikSSeries ");
00178      LOGDEBUG(logger,"Query Interval: "<< interval);
00179      LOGDEBUG(logger,"Ownadr: " << ownadr << " Commadr: " << commadr);
00180      cfghlp.GetConfig("comms", s, (string) "unset");
00181      LOGDEBUG(logger,"Communication: " << s);
00182 
00183 }
00184 
00185 CInverterSputnikSSeries::~CInverterSputnikSSeries()
00186 {
00187      // TODO Auto-generated destructor stub
00188 }
00189 
00190 bool CInverterSputnikSSeries::CheckConfig()
00191 {
00192      string setting;
00193      string str;
00194 
00195      bool fail = false;
00196 
00197      CConfigHelper hlp(configurationpath);
00198      fail |= (true != hlp.CheckConfig("comms", Setting::TypeString));
00199      // Note: Queryinterval is optional. But CConfigHelper handle also opt.
00200      // parameters and checks for type.
00201      fail
00202                |= (true != hlp.CheckConfig("queryinterval", Setting::TypeFloat,
00203                          true));
00204      fail |= (true != hlp.CheckConfig("commadr", Setting::TypeInt));
00205 
00206      // Check config of the component, if already instanciated.
00207      if (connection) {
00208           fail |= (true != connection->CheckConfig());
00209      }
00210 
00211      LOGTRACE(logger, "Check Configuration result: " << !fail);
00212      return !fail;
00213 }
00214 
00225 unsigned int CInverterSputnikSSeries::CalcChecksum(const char *str, int len)
00226 {
00227      unsigned int chksum = 0;
00228      str++;
00229      do {
00230           chksum += *str++;
00231      } while (--len);
00232 
00233      return chksum;
00234 }
00235 
00236 void CInverterSputnikSSeries::ExecuteCommand(const ICommand *Command)
00237 {
00238      bool dbg = true;
00239 
00240      string commstring = "";
00241      string reccomm = "";
00242      ICommand *cmd;
00243      timespec ts;
00244 
00245      switch ((Commands) Command->getCmd())
00246      {
00247 
00248      case CMD_DISCONNECTED:
00249      {
00250           // DISCONNECTED: Error detected, the link to the com partner is down.
00251           // Action: Schedule connection retry in xxx seconds
00252           // Next-State: INIT (Try to connect)
00253           LOGTRACE(logger, "new state: CMD_DISCONNECTED");
00254 
00255           // Timeout on reception
00256           // we assume that we are now disconnected.
00257           // so lets schedule a reconnection.
00258           // TODO this time should be configurable.
00259           connection->Disconnect();
00260 
00261           // Tell everyone that all data is now invalid.
00262           CCapability *c = GetConcreteCapability(CAPA_INVERTER_DATASTATE);
00263           CValue<bool> *v = (CValue<bool> *) c->getValue();
00264           v->Set(false);
00265           c->Notify();
00266 
00267           cmd = new ICommand(CMD_INIT, this);
00268           timespec ts;
00269           ts.tv_sec = 15;
00270           ts.tv_nsec = 0;
00271           Registry::GetMainScheduler()->ScheduleWork(cmd, ts);
00272           break;
00273      }
00274 
00275      case CMD_INIT:
00276      {
00277           // INIT: Try to connect to the comm partner
00278           // Action Connection Attempt
00279           // Next-State: Wait4Connection
00280           LOGTRACE(logger, "new state: CMD_INIT");
00281 
00282           cmd = new ICommand(CMD_WAIT4CONNECTION, this);
00283           connection->Connect(cmd);
00284           break;
00285 
00286           // storage of objects in boost::any
00287           //cmd->addData("TEST", cmd);
00288           //cmd = boost::any_cast<ICommand*>(cmd->findData("TEST"));
00289      }
00290 
00291      case CMD_WAIT4CONNECTION:
00292      {
00293           LOGTRACE(logger, "new state: CMD_WAIT4CONNECTION");
00294 
00295           int err = -1;
00296           // WAIT4CONNECTION: Wait until connection is up of failed to set up
00297           // by the communication object.
00298           // Action: Check success/error flag
00299           // Next-State: Depending on success:
00300           //        success IDENTIFY_COMM
00301           //        error     DISCONNECTED
00302           try {
00303                err = boost::any_cast<int>(Command->findData(ICMD_ERRNO));
00304           } catch (...) {
00305                LOGDEBUG(logger,"CMD_WAIT4CONNECTION: unexpected exception");
00306                err = -1;
00307           }
00308 
00309           if (err < 0) {
00310                try {
00311                     LOGERROR(logger, "Error while connecting: " <<
00312                               boost::any_cast<string>(Command->findData(ICMD_ERRNO_STR)));
00313                } catch (...) {
00314                     LOGERROR(logger, "Unknown error while connecting.");
00315                }
00316 
00317                cmd = new ICommand(CMD_DISCONNECTED, this);
00318                Registry::GetMainScheduler()->ScheduleWork(cmd);
00319           } else {
00320                cmd = new ICommand(CMD_QUERY_IDENTIFY, this);
00321                Registry::GetMainScheduler()->ScheduleWork(cmd);
00322           }
00323      }
00324           break;
00325 
00326      case CMD_QUERY_IDENTIFY:
00327           LOGTRACE(logger, "new state: CMD_QUERY_IDENTIFY ");
00328 
00329           pushinverterquery(TYP);
00330           pushinverterquery(SWV);
00331           pushinverterquery(BUILDVER);
00332 
00333      case CMD_QUERY_POLL:
00334           LOGTRACE(logger, "new state: CMD_QUERY_POLL ");
00335 
00336           pushinverterquery(PAC);
00337           pushinverterquery(KHR);
00338           pushinverterquery(PIN);
00339           pushinverterquery(KT0);
00340           pushinverterquery(DDY);
00341           pushinverterquery(KYR);
00342           pushinverterquery(KMT);
00343           pushinverterquery(KDY);
00344           pushinverterquery(KT0);
00345           pushinverterquery(PRL);
00346 
00347           pushinverterquery(UDC);
00348           pushinverterquery(IDC);
00349           pushinverterquery(IL1);
00350           pushinverterquery(UL1);
00351           pushinverterquery(TKK);
00352           pushinverterquery(TMI);
00353           pushinverterquery(THR);
00354           pushinverterquery(TNF);
00355           pushinverterquery(SYS);
00356 
00357      case CMD_SEND_QUERIES:
00358      {
00359           LOGTRACE(logger, "new state: CMD_SEND_QUERIES ");
00360 
00361           commstring = assemblequerystring();
00362           LOGTRACE(logger, "Sending: " << commstring << " Len: "<< commstring.size());
00363 
00364           cmd = new ICommand(CMD_WAIT_SENT, this);
00365           connection->Send(commstring, cmd);
00366      }
00367           break;
00368 
00369      case CMD_WAIT_SENT:
00370      {
00371           LOGTRACE(logger, "new state: CMD_WAIT_SENT");
00372           int err;
00373           try {
00374                err = boost::any_cast<int>(Command->findData(ICMD_ERRNO));
00375           } catch (...) {
00376                LOGDEBUG(logger, "BUG: Unexpected exception.");
00377                err = -1;
00378           }
00379 
00380           if (err < 0) {
00381                cmd = new ICommand(CMD_DISCONNECTED, this);
00382                Registry::GetMainScheduler()->ScheduleWork(cmd);
00383                break;
00384           }
00385      }
00386      // Fall through ok.
00387 
00388      case CMD_WAIT_RECEIVE:
00389      {
00390           LOGTRACE(logger, "new state: CMD_WAIT_RECEIVE");
00391 
00392           cmd = new ICommand(CMD_EVALUATE_RECEIVE, this);
00393           connection->Receive(cmd);
00394      }
00395           break;
00396 
00397      case CMD_EVALUATE_RECEIVE:
00398      {
00399           LOGTRACE(logger, "new state: CMD_EVALUATE_RECEIVE");
00400 
00401           int err;
00402           std::string s;
00403           try {
00404                err = boost::any_cast<int>(Command->findData(ICMD_ERRNO));
00405           } catch (...) {
00406                LOGDEBUG(logger, "BUG: Unexpected exception.");
00407                err = -1;
00408           }
00409 
00410           if (err < 0) {
00411                // we do not differenziate the error here, a error is a error....
00412                cmd = new ICommand(CMD_DISCONNECTED, this);
00413                Registry::GetMainScheduler()->ScheduleWork(cmd);
00414                try {
00415                     s = boost::any_cast<std::string>(Command->findData(
00416                               ICMD_ERRNO_STR));
00417                     LOGERROR(logger, "Receive Error: " << s);
00418                } catch (...) {
00419                     LOGERROR(logger, "Receive Error: " << strerror(-err));
00420                }
00421                break;
00422           }
00423 
00424           try {
00425                s = boost::any_cast<std::string>(Command->findData(
00426                          ICONN_TOKEN_RECEIVE_STRING));
00427           } catch (...) {
00428                LOGDEBUG(logger, "Unexpected Exception");
00429                break;
00430           }
00431 
00432           LOGTRACE(logger, "Received :" << s << "len: " << s.size());
00433           if (logger.IsEnabled(ILogger::LL_TRACE)) {
00434                string st;
00435                char buf[32];
00436                for (unsigned int i = 0; i < s.size(); i++) {
00437                     sprintf(buf, "%02x", (unsigned char) s[i]);
00438                     st = st + buf;
00439                     if (i && i % 16 == 0)
00440                          st = st + "\n";
00441                     else
00442                          st = st + ' ';
00443                }
00444                LOGTRACE(logger, "Received in hex: "<< st );
00445           }
00446 
00447           int parseresult = parsereceivedstring(s);
00448           if (0 > parseresult ) {
00449                // Reconnect on parse errors.
00450                LOGERROR(logger, "Parse error while receiving.");
00451                LOGDEBUG(logger, "Received: " << s);
00452                cmd = new ICommand(CMD_DISCONNECTED, this);
00453                Registry::GetMainScheduler()->ScheduleWork(cmd);
00454                break;
00455           } else if ( parseresult == 0) {
00456                // The received data seems not to be for our inverter.
00457                // Can happen on a shared connection.
00458                // So lets wait again.
00459 
00460                ICommand *cmd = new ICommand(CMD_EVALUATE_RECEIVE, this);
00461                connection->Receive(cmd);
00462                break;
00463           }
00464 
00465           // check if there are queries left in this cycle.
00466           if (!cmdqueue.empty()) {
00467                cmd = new ICommand(CMD_SEND_QUERIES, this);
00468                // there are more. finish them before setting the data valid.
00469                Registry::GetMainScheduler()->ScheduleWork(cmd);
00470                break;
00471           }
00472 
00473           // TODO differenciate between identify query and "normal" runtime queries
00474           cmd = new ICommand(CMD_QUERY_POLL, this);
00475 
00476           CCapability *c = GetConcreteCapability(CAPA_INVERTER_DATASTATE);
00477           CValue<bool> *vb = (CValue<bool> *) c->getValue();
00478           vb->Set(true);
00479           c->Notify();
00480 
00481           c = GetConcreteCapability(CAPA_INVERTER_QUERYINTERVAL);
00482           CValue<float> *v = (CValue<float> *) c->getValue();
00483           ts.tv_sec = v->Get();
00484           ts.tv_nsec = ((v->Get() - ts.tv_sec) * 1e9);
00485           Registry::GetMainScheduler()->ScheduleWork(cmd, ts);
00486      }
00487           break;
00488 
00489      default:
00490           LOGFATAL(logger, "Unknown CMD received");
00491           abort();
00492 
00493      }
00494 
00495 }
00496 
00497 void CInverterSputnikSSeries::pushinverterquery(enum query q)
00498 {
00499      cmdqueue.push(q);
00500 }
00501 
00502 string CInverterSputnikSSeries::assemblequerystring()
00503 {
00504      if (cmdqueue.empty()) {
00505           LOGTRACE(logger, "assemblequerystring: empty task lisk.");
00506           return "";
00507      }
00508 
00509      int len = 0;
00510      int expectedanswerlen = 0;
00511      int currentport = 0;
00512      string nextcmd;
00513      string querystring, tmp;
00514      bool cont = true;
00515      char formatbuffer[256];
00516 
00517      do {
00518           switch (cmdqueue.front())
00519           {
00520 
00521           case TYP:
00522           {
00523                if (currentport && currentport != QUERY) {
00524                     // Changing ports are not supported.
00525                     // Different ports means a different message.
00526                     cont = false;
00527                     break;
00528                }
00529                currentport = QUERY;
00530                nextcmd = "TYP";
00531                expectedanswerlen += 9;
00532                break;
00533           }
00534 
00535           case SWV:
00536           {
00537                if (currentport && currentport != QUERY) {
00538                     cont = false;
00539                     break;
00540                }
00541                currentport = QUERY;
00542                nextcmd = "SWV";
00543                expectedanswerlen += 7;
00544                break;
00545           }
00546 
00547           case BUILDVER:
00548           {
00549                if (currentport && currentport != QUERY) {
00550                     cont = false;
00551                     break;
00552                }
00553                currentport = QUERY;
00554                nextcmd = "BDN";
00555                expectedanswerlen += 9;
00556                break;
00557           }
00558 
00559           case EC00:
00560           {
00561                if (currentport && currentport != QUERY) {
00562                     cont = false;
00563                     break;
00564                }
00565                currentport = QUERY;
00566                nextcmd = "EC00";
00567                expectedanswerlen += 27;
00568                break;
00569           }
00570 
00571           case EC01:
00572           {
00573                if (currentport && currentport != QUERY) {
00574                     cont = false;
00575                     break;
00576                }
00577                currentport = QUERY;
00578                nextcmd = "EC01";
00579                expectedanswerlen += 27;
00580                break;
00581           }
00582 
00583           case EC02:
00584           {
00585                if (currentport && currentport != QUERY) {
00586                     cont = false;
00587                     break;
00588                }
00589                currentport = QUERY;
00590                nextcmd = "EC02";
00591                expectedanswerlen += 27;
00592                break;
00593           }
00594 
00595           case EC03:
00596           {
00597 
00598                if (currentport && currentport != QUERY) {
00599                     cont = false;
00600                     break;
00601                }
00602                currentport = QUERY;
00603                nextcmd = "EC03";
00604                expectedanswerlen += 27;
00605                break;
00606           }
00607 
00608           case EC04:
00609           {
00610                if (currentport && currentport != QUERY) {
00611                     cont = false;
00612                     break;
00613                }
00614                currentport = QUERY;
00615                nextcmd = "EC04";
00616                expectedanswerlen += 27;
00617                break;
00618           }
00619 
00620           case EC05:
00621           {
00622                if (currentport && currentport != QUERY) {
00623                     cont = false;
00624                     break;
00625                }
00626                currentport = QUERY;
00627                nextcmd = "EC05";
00628                expectedanswerlen += 27;
00629                break;
00630           }
00631 
00632           case EC06:
00633           {
00634                if (currentport && currentport != QUERY) {
00635                     cont = false;
00636                     break;
00637                }
00638                currentport = QUERY;
00639                nextcmd = "EC06";
00640                expectedanswerlen += 27;
00641                break;
00642           }
00643 
00644           case EC07:
00645           {
00646                if (currentport && currentport != QUERY) {
00647                     cont = false;
00648                     break;
00649                }
00650                currentport = QUERY;
00651                nextcmd = "EC07";
00652                expectedanswerlen += 27;
00653                break;
00654           }
00655 
00656           case EC08:
00657           {
00658                if (currentport && currentport != QUERY) {
00659                     cont = false;
00660                     break;
00661                }
00662                currentport = QUERY;
00663                nextcmd = "EC08";
00664                expectedanswerlen += 27;
00665                break;
00666           }
00667 
00668           case PAC:
00669           {
00670                if (currentport && currentport != QUERY) {
00671                     cont = false;
00672                     break;
00673                }
00674                currentport = QUERY;
00675                nextcmd = "PAC";
00676                expectedanswerlen += 9;
00677                break;
00678           }
00679 
00680           case KHR:
00681           {
00682                if (currentport && currentport != QUERY) {
00683                     cont = false;
00684                     break;
00685                }
00686                currentport = QUERY;
00687                nextcmd = "KHR";
00688                expectedanswerlen += 9;
00689                break;
00690           }
00691 
00692           case DYR:
00693           {
00694                if (currentport && currentport != QUERY) {
00695                     cont = false;
00696                     break;
00697                }
00698                currentport = QUERY;
00699                nextcmd = "DYR";
00700                expectedanswerlen += 7;
00701                break;
00702           }
00703 
00704           case DMT:
00705           {
00706                if (currentport && currentport != QUERY) {
00707                     cont = false;
00708                     break;
00709                }
00710                currentport = QUERY;
00711                nextcmd = "DMT";
00712                // TODO check answer len
00713                expectedanswerlen += 7;
00714                break;
00715           }
00716 
00717           case DDY:
00718           {
00719                if (currentport && currentport != QUERY) {
00720                     cont = false;
00721                     break;
00722                }
00723                currentport = QUERY;
00724                nextcmd = "DDY";
00725                expectedanswerlen += 7;
00726                break;
00727           }
00728 
00729           case KYR:
00730           {
00731                if (currentport && currentport != QUERY) {
00732                     cont = false;
00733                     break;
00734                }
00735                currentport = QUERY;
00736                nextcmd = "KYR";
00737                expectedanswerlen += 9;
00738                break;
00739           }
00740 
00741           case KMT:
00742           {
00743                if (currentport && currentport != QUERY) {
00744                     cont = false;
00745                     break;
00746                }
00747                currentport = QUERY;
00748                nextcmd = "KMT";
00749                expectedanswerlen += 7;
00750                break;
00751           }
00752 
00753           case KDY:
00754           {
00755                if (currentport && currentport != QUERY) {
00756                     cont = false;
00757                     break;
00758                }
00759                currentport = QUERY;
00760                nextcmd = "KDY";
00761                expectedanswerlen += 10;
00762                break;
00763           }
00764 
00765           case KT0:
00766           {
00767                if (currentport && currentport != QUERY) {
00768                     cont = false;
00769                     break;
00770                }
00771                currentport = QUERY;
00772                nextcmd = "KT0";
00773                expectedanswerlen += 10;
00774                break;
00775           }
00776 
00777           case PIN:
00778           {
00779                if (currentport && currentport != QUERY) {
00780                     cont = false;
00781                     break;
00782                }
00783                currentport = QUERY;
00784                nextcmd = "PIN";
00785                expectedanswerlen += 9;
00786                break;
00787           }
00788 
00789           case TNF:
00790           {
00791                if (currentport && currentport != QUERY) {
00792                     cont = false;
00793                     break;
00794                }
00795                currentport = QUERY;
00796                nextcmd = "TNF";
00797                // TODO check answer len
00798                expectedanswerlen += 10;
00799                break;
00800           }
00801 
00802           case PRL:
00803           {
00804                if (currentport && currentport != QUERY) {
00805                     cont = false;
00806                     break;
00807                }
00808                currentport = QUERY;
00809                nextcmd = "RPL";
00810                // TODO check answer len
00811                expectedanswerlen += 10;
00812                break;
00813           }
00814 
00815           case UDC:
00816           {
00817                if (currentport && currentport != QUERY) {
00818                     cont = false;
00819                     break;
00820                }
00821                currentport = QUERY;
00822                nextcmd = "UDC";
00823                // TODO check answer len
00824                expectedanswerlen += 10;
00825                break;
00826           }
00827 
00828           case UL1:
00829           {
00830                if (currentport && currentport != QUERY) {
00831                     cont = false;
00832                     break;
00833                }
00834                currentport = QUERY;
00835                nextcmd = "UL1";
00836                // TODO check answer len
00837                expectedanswerlen += 10;
00838                break;
00839           }
00840 
00841           case UL2:
00842           {
00843                if (currentport && currentport != QUERY) {
00844                     cont = false;
00845                     break;
00846                }
00847                currentport = QUERY;
00848                nextcmd = "UL2";
00849                // TODO check answer len
00850                expectedanswerlen += 10;
00851                break;
00852           }
00853 
00854           case UL3:
00855           {
00856                if (currentport && currentport != QUERY) {
00857                     cont = false;
00858                     break;
00859                }
00860                currentport = QUERY;
00861                nextcmd = "UL3";
00862                // TODO check answer len
00863                expectedanswerlen += 10;
00864                break;
00865           }
00866 
00867           case IDC:
00868           {
00869                if (currentport && currentport != QUERY) {
00870                     cont = false;
00871                     break;
00872                }
00873                currentport = QUERY;
00874                nextcmd = "IDC";
00875                // TODO check answer len
00876                expectedanswerlen += 10;
00877                break;
00878           }
00879 
00880           case IL1:
00881           {
00882                if (currentport && currentport != QUERY) {
00883                     cont = false;
00884                     break;
00885                }
00886                currentport = QUERY;
00887                nextcmd = "IL1";
00888                // TODO check answer len
00889                expectedanswerlen += 10;
00890                break;
00891           }
00892 
00893           case IL2:
00894           {
00895                if (currentport && currentport != QUERY) {
00896                     cont = false;
00897                     break;
00898                }
00899                currentport = QUERY;
00900                nextcmd = "IL2";
00901                // TODO check answer len
00902                expectedanswerlen += 10;
00903                break;
00904           }
00905 
00906           case IL3:
00907           {
00908                if (currentport && currentport != QUERY) {
00909                     cont = false;
00910                     break;
00911                }
00912                currentport = QUERY;
00913                nextcmd = "IL3";
00914                // TODO check answer len
00915                expectedanswerlen += 10;
00916                break;
00917           }
00918 
00919           case TKK:
00920           {
00921                if (currentport && currentport != QUERY) {
00922                     cont = false;
00923                     break;
00924                }
00925                currentport = QUERY;
00926                nextcmd = "TKK";
00927                // TODO check answer len
00928                expectedanswerlen += 10;
00929                break;
00930           }
00931 
00932           case TK2:
00933           {
00934                if (currentport && currentport != QUERY) {
00935                     cont = false;
00936                     break;
00937                }
00938                currentport = QUERY;
00939                nextcmd = "TK2";
00940                // TODO check answer len
00941                expectedanswerlen += 10;
00942                break;
00943           }
00944 
00945           case TK3:
00946           {
00947                if (currentport && currentport != QUERY) {
00948                     cont = false;
00949                     break;
00950                }
00951                currentport = QUERY;
00952                nextcmd = "TK3";
00953                // TODO check answer len
00954                expectedanswerlen += 10;
00955                break;
00956           }
00957 
00958           case TMI:
00959           {
00960                if (currentport && currentport != QUERY) {
00961                     cont = false;
00962                     break;
00963                }
00964                currentport = QUERY;
00965                nextcmd = "TMI";
00966                // TODO check answer len
00967                expectedanswerlen += 10;
00968                break;
00969           }
00970 
00971           case THR:
00972           {
00973                if (currentport && currentport != QUERY) {
00974                     cont = false;
00975                     break;
00976                }
00977                currentport = QUERY;
00978                nextcmd = "THR";
00979                // TODO check answer len
00980                expectedanswerlen += 10;
00981                break;
00982           }
00983 
00984           case SYS:
00985           {
00986                if (currentport && currentport != QUERY) {
00987                     cont = false;
00988                     break;
00989                }
00990                currentport = QUERY;
00991                nextcmd = "SYS";
00992                // TODO check answer len
00993                expectedanswerlen += 10;
00994                break;
00995           }
00996 
00997           }
00998 
00999           if (cont) {
01000                // check if command will fit into max. telegram len
01001                // note: 255 = max len, 15 = header "{xx;yy;zz|pppp:", 6 = tail "|CHKS}"
01002                // (plus one byte for the seperator.)
01003                // note: this is not squeezed to its end, as we will usually not
01004                // fill up to 255 bytes because lacking commands...
01005                if (nextcmd.length() + len <= (255 - 15 - 6) - 1 &&
01006                // use our assumption to limit the expected receive telegram
01007                          // add a safety margin of 10.
01008                          expectedanswerlen <= (255 - 15 - 6 - 1 - 10)) {
01009                     // Check if we need to insert a seperator.
01010                     if (len) {
01011                          querystring += ";";
01012                          len++;
01013                     }
01014                     // Add the prepared command and remove it from the queue.
01015                     querystring += nextcmd;
01016                     len += nextcmd.length();
01017                     cmdqueue.pop();
01018                } else {
01019                     cont = false;
01020                }
01021           }
01022      } while (cont && !cmdqueue.empty());
01023 
01024      sprintf(formatbuffer, "%X:", currentport);
01025      len = strlen(formatbuffer) + querystring.length() + 10 + 6;
01026 
01027      // finally prepare Header "{<from>;<to>;<len>|<port>:"
01028      sprintf(formatbuffer, "{%02X;%02X;%02X|%X:", ownadr, commadr, len,
01029                currentport);
01030 
01031      tmp = formatbuffer + querystring + '|';
01032      querystring = tmp;
01033 
01034      sprintf(formatbuffer, "%04X}", CalcChecksum(querystring.c_str(),
01035                querystring.length()));
01036 
01037      querystring += formatbuffer;
01038      return querystring;
01039 }
01040 
01041 int CInverterSputnikSSeries::parsereceivedstring(const string & s)
01042 {
01043 
01044      unsigned int i;
01045 
01046      // check for basic constraints...
01047      if (s[0] != '{' || s[s.length() - 1] != '}')
01048           return -1;
01049 
01050      // tokenizer (taken from
01051      // http://oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-7.html
01052      // but  modified.
01053 
01054      // we take the received string and "split" it into smaller pieces.
01055      // the pieces ("tokens") then assemble one single information to be
01056      // for this, we split by:
01057      // ";" "|" ":" (and "{}")
01058 
01059      vector<string> tokens;
01060      char delimiters[] = "{;|:}";
01061      tokenizer(delimiters, s, tokens);
01062 
01063      // Debug: Print all received tokens
01064 #if defined DEBUG_TOKENIZER
01065      if (logger.IsEnabled(ILogger::LL_TRACE)) {
01066           std::stringstream ss;
01067           vector<string>::iterator it;
01068           for (i = 0, it = tokens.begin(); it != tokens.end() - 1; it++) {
01069                ss << i++ << ": " << (*it) << "\tlen: "
01070                << (*it).length() << endl;
01071           }
01072           LOGTRACE(logger,ss);
01073      }
01074 #endif
01075 
01076      unsigned int tmp;
01077      if (1 != sscanf(tokens.back().c_str(), "%x", &tmp)) {
01078           LOGDEBUG(logger, "could not parse checksum. Token was:" );
01079           return -1;
01080      }
01081 
01082      if (tmp != CalcChecksum(s.c_str(), s.length() - 6)) {
01083           LOGDEBUG(logger, "Checksum error on received telegram");
01084           return -1;
01085      }
01086 
01087      if (1 != sscanf(tokens[0].c_str(), "%x", &tmp)) {
01088           LOGDEBUG(logger, " could not parse from address");
01089           return -1;
01090      }
01091 
01092      if (tmp != commadr) {
01093           LOGDEBUG(logger, "Received string is not for us: Wrong Sender");
01094           return 0;
01095      }
01096 
01097      if (1 != sscanf(tokens[1].c_str(), "%x", &tmp)) {
01098           LOGDEBUG(logger, "could not parse to-address");
01099           return -1;
01100      }
01101 
01102      if (tmp != ownadr) {
01103           LOGDEBUG(logger, "Received string is not for us: Wrong receiver");
01104           return 0;
01105      }
01106 
01107      if (1 != sscanf(tokens[2].c_str(), "%x", &tmp)) {
01108           LOGDEBUG(logger, "could not parse telegram length");
01109           return -1;
01110      }
01111 
01112      if (tmp != s.length()) {
01113           LOGDEBUG(logger, "wrong telegram length ");
01114           return -1;
01115      }
01116 
01117      // TODO FIXME: Currently the data port is simply "ignored"
01118      // parsetoken should get a second argument to get the port infos.
01119 
01120      int  ret = 1;
01121      for (i = 4; i < tokens.size() - 1; i++) {
01122           if (!parsetoken(tokens[i])) {
01123                LOGDEBUG(logger,
01124                          "BUG: Parse Error at token " << tokens[i]
01125                          << ". Received: " << s << "If the token is unknown or you subject a bug, please report it giving the  token ans received string"
01126                );
01127                ret = -1;
01128           }
01129      }
01130      return ret;
01131 }
01132 
01133 bool CInverterSputnikSSeries::parsetoken(string token)
01134 {
01135 
01136      vector<string> subtokens;
01137      const char delimiters[] = "=,";
01138      tokenizer(delimiters, token, subtokens);
01139 
01140      // TODO rewrite this section: Lookup the strings and functions in a table, call
01141      // by function pointer.
01142 
01143      if (subtokens[0] == "TYP") {
01144           return token_TYP(subtokens);
01145      }
01146 
01147      if (subtokens[0] == "SWV") {
01148           return token_SWVER(subtokens);
01149      }
01150 
01151      if (subtokens[0] == "BDN") {
01152           return token_BUILDVER(subtokens);
01153      }
01154 
01155      if (subtokens[0].substr(0, 2) == "EC") {
01156           return token_ECxx(subtokens);
01157      }
01158 
01159      if (subtokens[0] == "PAC") {
01160           return token_PAC(subtokens);
01161      }
01162 
01163      if (subtokens[0] == "KHR") {
01164           return token_KHR(subtokens);
01165      }
01166 
01167      if (subtokens[0] == "DYR") {
01168           return token_DYR(subtokens);
01169      }
01170 
01171      if (subtokens[0] == "DMT") {
01172           return token_DMT(subtokens);
01173      }
01174 
01175      if (subtokens[0] == "DDY") {
01176           return token_DDY(subtokens);
01177      }
01178 
01179      if (subtokens[0] == "KYR") {
01180           return token_KYR(subtokens);
01181      }
01182 
01183      if (subtokens[0] == "KMT") {
01184           return token_KMT(subtokens);
01185      }
01186 
01187      if (subtokens[0] == "KDY") {
01188           return token_KDY(subtokens);
01189      }
01190 
01191      if (subtokens[0] == "KT0") {
01192           return token_KT0(subtokens);
01193      }
01194 
01195      if (subtokens[0] == "PIN") {
01196           return token_PIN(subtokens);
01197      }
01198 
01199      if (subtokens[0] == "TNF") {
01200           return token_TNF(subtokens);
01201      }
01202      if (subtokens[0] == "PRL") {
01203           return token_PRL(subtokens);
01204      }
01205      if (subtokens[0] == "UDC") {
01206           return token_UDC(subtokens);
01207      }
01208 
01209      if (subtokens[0] == "UL1") {
01210           return token_UL1(subtokens);
01211      }
01212 
01213      if (subtokens[0] == "UL2") {
01214           return token_UL2(subtokens);
01215      }
01216 
01217      if (subtokens[0] == "UL3") {
01218           return token_UL3(subtokens);
01219      }
01220 
01221      if (subtokens[0] == "IDC") {
01222           return token_IDC(subtokens);
01223      }
01224      if (subtokens[0] == "IL1") {
01225           return token_IL1(subtokens);
01226      }
01227 
01228      if (subtokens[0] == "IL2") {
01229           return token_IL2(subtokens);
01230      }
01231 
01232      if (subtokens[0] == "IL3") {
01233           return token_IL3(subtokens);
01234      }
01235 
01236      if (subtokens[0] == "TKK") {
01237           return token_TKK(subtokens);
01238      }
01239 
01240      if (subtokens[0] == "TK2") {
01241           return token_TK2(subtokens);
01242      }
01243 
01244      if (subtokens[0] == "TK3") {
01245           return token_TK3(subtokens);
01246      }
01247 
01248      if (subtokens[0] == "TMI") {
01249           return token_TMI(subtokens);
01250      }
01251 
01252      if (subtokens[0] == "THR") {
01253           return token_THR(subtokens);
01254      }
01255 
01256      if (subtokens[0] == "SYS") {
01257           return token_SYS(subtokens);
01258      }
01259 
01260      return true;
01261 }
01262 
01263 void CInverterSputnikSSeries::tokenizer(const char *delimiters,
01264           const string& s, vector<string> &tokens)
01265 {
01266      unsigned int i;
01267 
01268      string::size_type lastPos = 0;
01269      string::size_type pos = 0;
01270 
01271      i = 0;
01272      // Skip tokens at the start of the string
01273      do {
01274           if (s[lastPos] == delimiters[i]) {
01275                lastPos++;
01276                i = 0;
01277           }
01278      } while (++i < strlen(delimiters));
01279 
01280      pos = lastPos;
01281 
01282      // get the first substring by finding the "second" delimiter
01283      i = lastPos;
01284 
01285      do {
01286           unsigned int tmp;
01287           tmp = s.find_first_of(delimiters[i], lastPos);
01288           if (tmp < pos)
01289                pos = tmp;
01290      } while (++i < strlen(delimiters));
01291 
01292      while (s.length() > pos && s.length() > lastPos) {
01293           unsigned int tmp, tmp2;
01294 
01295           if (pos - lastPos) {
01296                tokens.push_back(s.substr(lastPos, pos - lastPos));
01297           }
01298           lastPos = pos;
01299 
01300           // Skip delimiters.
01301           i = 0;
01302           do {
01303                if (s[lastPos] == delimiters[i]) {
01304                     lastPos++;
01305                     i = 0;
01306                }
01307 
01308           } while (++i < strlen(delimiters));
01309 
01310           // Find next "delimiter"
01311           i = 0;
01312           tmp2 = -1;
01313           do {
01314                tmp = s.find_first_of(delimiters[i], lastPos);
01315                if (tmp < tmp2)
01316                     tmp2 = tmp;
01317           } while (++i < strlen(delimiters));
01318           pos = tmp2;
01319      }
01320 
01321      // Check if we have an "end-token" (not seperated)
01322      if (lastPos != s.length()) {
01323           tokens.push_back(s.substr(lastPos, s.length() - lastPos));
01324      }
01325 }
01326 
01327 bool CInverterSputnikSSeries::token_TYP(const vector<string> & tokens)
01328 {
01329 
01330      string strmodel;
01331      unsigned int i = 0;
01332 
01333      // Check syntax
01334      if (tokens.size() != 2)
01335           return false;
01336      unsigned int model;
01337      sscanf(tokens[1].c_str(), "%x", &model);
01338 
01339      do {
01340           if (model_lookup[i].typ == model)
01341                break;
01342      } while (model_lookup[++i].typ != (unsigned int) -1);
01343 
01344      if (model_lookup[i].typ == (unsigned int) -1) {
01345           LOGWARN(logger, "Identified a " << model_lookup[i].description);
01346           LOGWARN(logger, "Received TYP was " << tokens[0] << "=" << tokens[1]);
01347      }
01348 
01349      // lookup if we already know that information.
01350      CCapability *cap = GetConcreteCapability(CAPA_INVERTER_MODEL);
01351 
01352      if (!cap) {
01353           string s;
01354           IValue *v;
01355           CCapability *c;
01356           s = CAPA_INVERTER_MODEL;
01357           v = IValue::Factory(CAPA_INVERTER_MODEL_TYPE);
01358           ((CValue<string>*) v)->Set(model_lookup[i].description);
01359           c = new CCapability(s, v, this);
01360           AddCapability(s, c);
01361 
01362           // TODO: Check if we schould derefer (using a scheduled work) this.
01363           cap = GetConcreteCapability(CAPA_CAPAS_UPDATED);
01364           cap->Notify();
01365      }
01366      // Capa already in the list. Check if we need to update it.
01367      else if (cap->getValue()->GetType() == CAPA_INVERTER_MODEL_TYPE) {
01368           CValue<string> *val = (CValue<string>*) cap->getValue();
01369           if (model_lookup[i].description != val->Get()) {
01370 
01371                LOGDEBUG(logger, "WEIRD: Updating inverter type from "
01372                          << val->Get() << " to "
01373                          << model_lookup[i].description);
01374 
01375                val->Set(model_lookup[i].description);
01376                cap->Notify();
01377           }
01378      } else {
01379           LOGDEBUG(logger, "BUG: " << CAPA_INVERTER_MODEL << " not a string ");
01380      }
01381 
01382      return true;
01383 }
01384 
01385 bool CInverterSputnikSSeries::token_SWVER(const vector<string> & tokens)
01386 {
01387      int tmp;
01388      string ver;
01389 
01390      // Check syntax
01391      if (tokens.size() != 2)
01392           return false;
01393 
01394      sscanf(tokens[1].c_str(), "%x", &tmp);
01395 
01396      // Been there, seen that.
01397      if (swversion == tmp)
01398           return true;
01399      swversion = tmp;
01400 
01401      create_versioncapa();
01402 
01403      return true;
01404 }
01405 
01406 bool CInverterSputnikSSeries::token_BUILDVER(const vector<string> & tokens)
01407 {
01408      int tmp;
01409      string ver;
01410 
01411      // Check syntax
01412      if (tokens.size() != 2)
01413           return false;
01414 
01415      sscanf(tokens[1].c_str(), "%x", &tmp);
01416 
01417      // Been there, seen that.
01418      if (swbuild == tmp)
01419           return true;
01420 
01421      swbuild = tmp;
01422      create_versioncapa();
01423 
01424      return true;
01425 }
01426 
01427 bool CInverterSputnikSSeries::token_ECxx(const vector<string> & tokens)
01428 {
01429      // FIXME TODO Implement me!
01430      // Will be implemented later, as currently not-so-important
01431      // (and just unsure, how to handle the different entries. Probably as an ring-
01432      // buffer, with just updating the last.
01433      return true;
01434 }
01435 
01436 bool CInverterSputnikSSeries::token_PAC(const vector<string> & tokens)
01437 {
01438      if (tokens.size() != 2)
01439           return false;
01440 
01441      unsigned int pac;
01442      float fpac;
01443      sscanf(tokens[1].c_str(), "%x", &pac);
01444 
01445      fpac = pac / 2.0;
01446 
01447      // lookup if we already know that information.
01448      CCapability *cap = GetConcreteCapability(CAPA_INVERTER_ACPOWER_TOTAL);
01449 
01450      if (!cap) {
01451           string s;
01452           IValue *v;
01453           CCapability *c;
01454           s = CAPA_INVERTER_ACPOWER_TOTAL;
01455           v = IValue::Factory(CAPA_INVERTER_ACPOWER_TOTAL_TYPE);
01456           ((CValue<float>*) v)->Set(fpac);
01457           c = new CCapability(s, v, this);
01458           AddCapability(s, c);
01459 
01460           // TODO: Check if we schould derefer (using a scheduled work) this.
01461           cap = GetConcreteCapability(CAPA_CAPAS_UPDATED);
01462           cap->Notify();
01463      }
01464      // Capa already in the list. Check if we need to update it.
01465      else if (cap->getValue()->GetType() == CAPA_INVERTER_ACPOWER_TOTAL_TYPE) {
01466           CValue<float> *val = (CValue<float>*) cap->getValue();
01467 
01468           if (val -> Get() != fpac) {
01469                val->Set(fpac);
01470                cap->Notify();
01471           }
01472      } else {
01473           LOGDEBUG(logger, "BUG: " << CAPA_INVERTER_ACPOWER_TOTAL
01474                     << " not a float ");
01475      }
01476 
01477      return true;
01478 }
01479 
01480 bool CInverterSputnikSSeries::token_KHR(const vector<string> & tokens)
01481 {
01482      // Power-On-Hours
01483      if (tokens.size() != 2)
01484           return false;
01485 
01486      unsigned int tmp;
01487      float f;
01488      sscanf(tokens[1].c_str(), "%x", &tmp);
01489 
01490      f = tmp;
01491 
01492      // lookup if we already know that information.
01493      CCapability *cap = GetConcreteCapability(CAPA_INVERTER_PON_HOURS);
01494 
01495      if (!cap) {
01496           string s;
01497           IValue *v;
01498           CCapability *c;
01499           s = CAPA_INVERTER_PON_HOURS;
01500           v = IValue::Factory(CAPA_INVERTER_PON_HOURS_TYPE);
01501           ((CValue<float>*) v)->Set(f);
01502           c = new CCapability(s, v, this);
01503           AddCapability(s, c);
01504 
01505           // TODO: Check if we schould derefer (using a scheduled work) this.
01506           cap = GetConcreteCapability(CAPA_CAPAS_UPDATED);
01507           cap->Notify();
01508      }
01509      // Capa already in the list. Check if we need to update it.
01510      else if (cap->getValue()->GetType() == CAPA_INVERTER_PON_HOURS_TYPE) {
01511           CValue<float> *val = (CValue<float>*) cap->getValue();
01512           if (val -> Get() != f) {
01513                val->Set(f);
01514                cap->Notify();
01515           }
01516      } else {
01517           LOGDEBUG(logger, "BUG: " << CAPA_INVERTER_PON_HOURS << " not a float ");
01518      }
01519 
01520      return true;
01521 }
01522 
01523 bool CInverterSputnikSSeries::token_DYR(const vector<string> & tokens)
01524 {
01525      // FIXME TODO Implement me!
01526      return true;
01527 }
01528 
01529 bool CInverterSputnikSSeries::token_DMT(const vector<string> & tokens)
01530 {
01531      // FIXME TODO Implement me!
01532      return true;
01533 }
01534 
01535 bool CInverterSputnikSSeries::token_DDY(const vector<string> & tokens)
01536 {
01537      // FIXME TODO Implement me!
01538      return true;
01539 }
01540 
01541 bool CInverterSputnikSSeries::token_KYR(const vector<string> & tokens)
01542 {
01543      // Unit kwH
01544      if (tokens.size() != 2)
01545           return false;
01546 
01547      unsigned int raw;
01548      float kwh;
01549      sscanf(tokens[1].c_str(), "%x", &raw);
01550 
01551      kwh = raw;
01552 
01553      // lookup if we already know that information.
01554      CCapability *cap = GetConcreteCapability(CAPA_INVERTER_KWH_Y2D);
01555 
01556      if (!cap) {
01557           string s;
01558           IValue *v;
01559           CCapability *c;
01560           s = CAPA_INVERTER_KWH_Y2D;
01561           v = IValue::Factory(CAPA_INVERTER_KWH_Y2D_TYPE);
01562           ((CValue<float>*) v)->Set(kwh);
01563           c = new CCapability(s, v, this);
01564           AddCapability(s, c);
01565 
01566           // TODO: Check if we schould derefer (using a scheduled work) this.
01567           cap = GetConcreteCapability(CAPA_CAPAS_UPDATED);
01568           cap->Notify();
01569      }
01570      // Capa already in the list. Check if we need to update it.
01571      else if (cap->getValue()->GetType() == CAPA_INVERTER_KWH_Y2D_TYPE) {
01572           CValue<float> *val = (CValue<float>*) cap->getValue();
01573 
01574           if (val -> Get() != kwh) {
01575                val->Set(kwh);
01576                cap->Notify();
01577           }
01578      } else {
01579           LOGDEBUG(logger, "BUG: " << CAPA_INVERTER_KWH_Y2D << " not a float ");
01580      }
01581 
01582      return true;
01583 }
01584 
01585 bool CInverterSputnikSSeries::token_KMT(const vector<string> & tokens)
01586 {
01587      // Unit kwH
01588      if (tokens.size() != 2)
01589           return false;
01590 
01591      unsigned int raw;
01592      float kwh;
01593      sscanf(tokens[1].c_str(), "%x", &raw);
01594 
01595      kwh = raw;
01596 
01597      // lookup if we already know that information.
01598      CCapability *cap = GetConcreteCapability(CAPA_INVERTER_KWH_M2D);
01599 
01600      if (!cap) {
01601           string s;
01602           IValue *v;
01603           CCapability *c;
01604           s = CAPA_INVERTER_KWH_M2D;
01605           v = IValue::Factory(CAPA_INVERTER_KWH_M2D_TYPE);
01606           ((CValue<float>*) v)->Set(kwh);
01607           c = new CCapability(s, v, this);
01608           AddCapability(s, c);
01609 
01610           // TODO: Check if we schould derefer (using a scheduled work) this.
01611           cap = GetConcreteCapability(CAPA_CAPAS_UPDATED);
01612           cap->Notify();
01613      }
01614      // Capa already in the list. Check if we need to update it.
01615      else if (cap->getValue()->GetType() == CAPA_INVERTER_KWH_M2D_TYPE) {
01616           CValue<float> *val = (CValue<float>*) cap->getValue();
01617 
01618           if (val -> Get() != kwh) {
01619                val->Set(kwh);
01620                cap->Notify();
01621           }
01622      } else {
01623           LOGDEBUG(logger, "BUG: " << CAPA_INVERTER_KWH_M2D << " not a float ");
01624      }
01625 
01626      return true;
01627 }
01628 
01629 bool CInverterSputnikSSeries::token_KDY(const vector<string> & tokens)
01630 {
01631      // Unit 0.1 kwH
01632      if (tokens.size() != 2)
01633           return false;
01634 
01635      unsigned int raw;
01636      float kwh;
01637      sscanf(tokens[1].c_str(), "%x", &raw);
01638 
01639      kwh = raw / 10.0;
01640 
01641      // lookup if we already know that information.
01642      CCapability *cap = GetConcreteCapability(CAPA_INVERTER_KWH_2D);
01643      if (!cap) {
01644           string s;
01645           IValue *v;
01646           CCapability *c;
01647           s = CAPA_INVERTER_KWH_2D;
01648           v = IValue::Factory(CAPA_INVERTER_KWH_2D_TYPE);
01649           ((CValue<float>*) v)->Set(kwh);
01650           c = new CCapability(s, v, this);
01651           AddCapability(s, c);
01652 
01653           // TODO: Check if we schould derefer (using a scheduled work) this.
01654           cap = GetConcreteCapability(CAPA_CAPAS_UPDATED);
01655           cap->Notify();
01656      }
01657      // Capa already in the list. Check if we need to update it.
01658      else if (cap->getValue()->GetType() == CAPA_INVERTER_KWH_2D_TYPE) {
01659           CValue<float> *val = (CValue<float>*) cap->getValue();
01660 
01661           if (val -> Get() != kwh) {
01662                val->Set(kwh);
01663                cap->Notify();
01664           }
01665      } else {
01666           LOGDEBUG(logger, "BUG: " << CAPA_INVERTER_KWH_2D << " not a float ");
01667      }
01668 
01669      return true;
01670 }
01671 
01672 bool CInverterSputnikSSeries::token_KT0(const vector<string> & tokens)
01673 {
01674      // FIXME TODO Implement me!
01675      // Unit kwH
01676      if (tokens.size() != 2)
01677           return false;
01678 
01679      unsigned int raw;
01680      float kwh;
01681      sscanf(tokens[1].c_str(), "%x", &raw);
01682 
01683      kwh = raw;
01684 
01685      // lookup if we already know that information.
01686      CCapability *cap = GetConcreteCapability(CAPA_INVERTER_KWH_TOTAL_NAME);
01687 
01688      if (!cap) {
01689           string s;
01690           IValue *v;
01691           CCapability *c;
01692           s = CAPA_INVERTER_KWH_TOTAL_NAME;
01693           v = IValue::Factory(CAPA_INVERTER_KWH_TOTAL_TYPE);
01694           ((CValue<float>*) v)->Set(kwh);
01695           c = new CCapability(s, v, this);
01696           AddCapability(s, c);
01697 
01698           // TODO: Check if we schould derefer (using a scheduled work) this.
01699           cap = GetConcreteCapability(CAPA_CAPAS_UPDATED);
01700           cap->Notify();
01701      }
01702      // Capa already in the list. Check if we need to update it.
01703      else if (cap->getValue()->GetType() == CAPA_INVERTER_KWH_TOTAL_TYPE) {
01704           CValue<float> *val = (CValue<float>*) cap->getValue();
01705 
01706           if (val -> Get() != kwh) {
01707                val->Set(kwh);
01708                cap->Notify();
01709           }
01710      } else {
01711           LOGDEBUG(logger, "BUG: " << CAPA_INVERTER_KWH_TOTAL_NAME
01712                     << " not a float ");
01713      }
01714 
01715      return true;
01716 }
01717 
01718 bool CInverterSputnikSSeries::token_PIN(const vector<string> & tokens)
01719 {
01720      // Unit 0.5 Watts
01721      if (tokens.size() != 2)
01722           return false;
01723 
01724      unsigned int raw;
01725      float f;
01726      sscanf(tokens[1].c_str(), "%x", &raw);
01727 
01728      f = raw * 0.5;
01729      // lookup if we already know that information.
01730      CCapability *cap = GetConcreteCapability(CAPA_INVERTER_INSTALLEDPOWER_NAME);
01731 
01732      if (!cap) {
01733           string s;
01734           IValue *v;
01735           CCapability *c;
01736           s = CAPA_INVERTER_INSTALLEDPOWER_NAME;
01737           v = IValue::Factory(CAPA_INVERTER_INSTALLEDPOWER_TYPE);
01738           ((CValue<float>*) v)->Set(f);
01739           c = new CCapability(s, v, this);
01740           AddCapability(s, c);
01741 
01742           // TODO: Check if we schould derefer (using a scheduled work) this.
01743           cap = GetConcreteCapability(CAPA_CAPAS_UPDATED);
01744           cap->Notify();
01745      }
01746      // Capa already in the list. Check if we need to update it.
01747      else if (cap->getValue()->GetType() == CAPA_INVERTER_INSTALLEDPOWER_TYPE) {
01748           CValue<float> *val = (CValue<float>*) cap->getValue();
01749 
01750           if (val -> Get() != f) {
01751                val->Set(f);
01752                cap->Notify();
01753           }
01754      } else {
01755           LOGDEBUG(logger, "BUG: " << CAPA_INVERTER_INSTALLEDPOWER_NAME
01756                     << " not a float ");
01757      }
01758 
01759      return true;
01760 }
01761 
01762 bool CInverterSputnikSSeries::token_TNF(const vector<string> & tokens)
01763 {
01764      // FIXME TODO Implement me!
01765      // Unit us
01766      // f = 1 / T , T = x / 1E6
01767      if (tokens.size() != 2)
01768           return false;
01769 
01770      unsigned int raw;
01771      float f;
01772      sscanf(tokens[1].c_str(), "%x", &raw);
01773 
01774      f = raw / 100.0;
01775      // lookup if we already know that information.
01776      CCapability *cap = GetConcreteCapability(CAPA_INVERTER_NET_FREQUENCY_NAME);
01777 
01778      if (!cap) {
01779           string s;
01780           IValue *v;
01781           CCapability *c;
01782           s = CAPA_INVERTER_NET_FREQUENCY_NAME;
01783           v = IValue::Factory(CAPA_INVERTER_NET_FREQUENCY_TYPE);
01784           ((CValue<float>*) v)->Set(f);
01785           c = new CCapability(s, v, this);
01786           AddCapability(s, c);
01787 
01788           // TODO: Check if we schould derefer (using a scheduled work) this.
01789           cap = GetConcreteCapability(CAPA_CAPAS_UPDATED);
01790           cap->Notify();
01791      }
01792      // Capa already in the list. Check if we need to update it.
01793      else if (cap->getValue()->GetType() == CAPA_INVERTER_NET_FREQUENCY_TYPE) {
01794           CValue<float> *val = (CValue<float>*) cap->getValue();
01795 
01796           if (val -> Get() != f) {
01797                val->Set(f);
01798                cap->Notify();
01799           }
01800      } else {
01801           LOGDEBUG(logger, "BUG: " << CAPA_INVERTER_NET_FREQUENCY_NAME
01802                     << " not a float ");
01803      }
01804 
01805      return true;
01806 }
01807 
01808 bool CInverterSputnikSSeries::token_PRL(const vector<string> & tokens)
01809 {
01810      // Unit 1 %
01811      if (tokens.size() != 2)
01812           return false;
01813 
01814      unsigned int raw;
01815      float f;
01816      sscanf(tokens[1].c_str(), "%x", &raw);
01817 
01818      f = raw;
01819      // lookup if we already know that information.
01820      CCapability *cap = GetConcreteCapability(CAPA_INVERTER_RELPOWER_NAME);
01821 
01822      if (!cap) {
01823           string s;
01824           IValue *v;
01825           CCapability *c;
01826           s = CAPA_INVERTER_RELPOWER_NAME;
01827           v = IValue::Factory(CAPA_INVERTER_RELPOWER_TYPE);
01828           ((CValue<float>*) v)->Set(f);
01829           c = new CCapability(s, v, this);
01830           AddCapability(s, c);
01831 
01832           // TODO: Check if we schould derefer (using a scheduled work) this.
01833           cap = GetConcreteCapability(CAPA_CAPAS_UPDATED);
01834           cap->Notify();
01835      }
01836      // Capa already in the list. Check if we need to update it.
01837      else if (cap->getValue()->GetType() == CAPA_INVERTER_RELPOWER_TYPE) {
01838           CValue<float> *val = (CValue<float>*) cap->getValue();
01839 
01840           if (val -> Get() != f) {
01841                val->Set(f);
01842                cap->Notify();
01843           }
01844      } else {
01845           LOGDEBUG(logger, "BUG: " << CAPA_INVERTER_RELPOWER_NAME
01846                     << " not a float ");
01847      }
01848 
01849      return true;
01850 
01851      // FIXME TODO Implement me!
01852 
01853      return true;
01854 }
01855 
01856 bool CInverterSputnikSSeries::token_UDC(const vector<string> & tokens)
01857 {
01858      // Unit 0.1 Volts
01859      if (tokens.size() != 2)
01860           return false;
01861 
01862      unsigned int raw;
01863      float f;
01864      sscanf(tokens[1].c_str(), "%x", &raw);
01865 
01866      f = raw * 0.1;
01867      // lookup if we already know that information.
01868      CCapability *cap = GetConcreteCapability(
01869                CAPA_INVERTER_INPUT_DC_VOLTAGE_NAME);
01870 
01871      if (!cap) {
01872           string s;
01873           IValue *v;
01874           CCapability *c;
01875           s = CAPA_INVERTER_INPUT_DC_VOLTAGE_NAME;
01876           v = IValue::Factory(CAPA_INVERTER_INPUT_DC_VOLTAGE_TYPE);
01877           ((CValue<float>*) v)->Set(f);
01878           c = new CCapability(s, v, this);
01879           AddCapability(s, c);
01880 
01881           // TODO: Check if we schould derefer (using a scheduled work) this.
01882           cap = GetConcreteCapability(CAPA_CAPAS_UPDATED);
01883           cap->Notify();
01884      }
01885      // Capa already in the list. Check if we need to update it.
01886      else if (cap->getValue()->GetType() == CAPA_INVERTER_INPUT_DC_VOLTAGE_TYPE) {
01887           CValue<float> *val = (CValue<float>*) cap->getValue();
01888 
01889           if (val -> Get() != f) {
01890                val->Set(f);
01891                cap->Notify();
01892           }
01893      } else {
01894           LOGDEBUG(logger, "BUG: " << CAPA_INVERTER_INPUT_DC_VOLTAGE_NAME
01895                     << " not a float ");
01896      }
01897 
01898      return true;
01899 }
01900 
01901 bool CInverterSputnikSSeries::token_UL1(const vector<string> & tokens)
01902 { // Unit 0.1 Volts
01903      if (tokens.size() != 2)
01904           return false;
01905 
01906      unsigned int raw;
01907      float f;
01908      sscanf(tokens[1].c_str(), "%x", &raw);
01909 
01910      f = raw * 0.1;
01911      // lookup if we already know that information.
01912      CCapability *cap =
01913                GetConcreteCapability(CAPA_INVERTER_GRID_AC_VOLTAGE_NAME);
01914 
01915      if (!cap) {
01916           string s;
01917           IValue *v;
01918           CCapability *c;
01919           s = CAPA_INVERTER_GRID_AC_VOLTAGE_NAME;
01920           v = IValue::Factory(CAPA_INVERTER_GRID_AC_VOLTAGE_TYPE);
01921           ((CValue<float>*) v)->Set(f);
01922           c = new CCapability(s, v, this);
01923           AddCapability(s, c);
01924 
01925           // TODO: Check if we schould derefer (using a scheduled work) this.
01926           cap = GetConcreteCapability(CAPA_CAPAS_UPDATED);
01927           cap->Notify();
01928      }
01929      // Capa already in the list. Check if we need to update it.
01930      else if (cap->getValue()->GetType() == CAPA_INVERTER_GRID_AC_VOLTAGE_TYPE) {
01931           CValue<float> *val = (CValue<float>*) cap->getValue();
01932 
01933           if (val -> Get() != f) {
01934                val->Set(f);
01935                cap->Notify();
01936           }
01937      } else {
01938           LOGDEBUG(logger, "BUG: " << CAPA_INVERTER_GRID_AC_VOLTAGE_NAME
01939                     << " not a float");
01940      }
01941 
01942      return true;
01943 }
01944 
01945 // TODO generate a more fitting concept for multi-phase invertes
01946 // like "pseudo-inverters"
01947 bool CInverterSputnikSSeries::token_UL2(const vector<string> & tokens)
01948 {
01949      // FIXME TODO Implement me!
01950      return true;
01951 }
01952 
01953 bool CInverterSputnikSSeries::token_UL3(const vector<string> & tokens)
01954 {
01955      // FIXME TODO Implement me!
01956      return true;
01957 }
01958 
01959 bool CInverterSputnikSSeries::token_IDC(const vector<string> & tokens)
01960 { // Unit 0.01 Amps
01961      if (tokens.size() != 2)
01962           return false;
01963 
01964      unsigned int raw;
01965      float f;
01966      sscanf(tokens[1].c_str(), "%x", &raw);
01967 
01968      f = raw * 0.01;
01969      // lookup if we already know that information.
01970      CCapability *cap = GetConcreteCapability(
01971                CAPA_INVERTER_INPUT_DC_CURRENT_NAME);
01972 
01973      if (!cap) {
01974           string s;
01975           IValue *v;
01976           CCapability *c;
01977           s = CAPA_INVERTER_INPUT_DC_CURRENT_NAME;
01978           v = IValue::Factory(CAPA_INVERTER_INPUT_DC_CURRENT_TYPE);
01979           ((CValue<float>*) v)->Set(f);
01980           c = new CCapability(s, v, this);
01981           AddCapability(s, c);
01982 
01983           // TODO: Check if we schould derefer (using a scheduled work) this.
01984           cap = GetConcreteCapability(CAPA_CAPAS_UPDATED);
01985           cap->Notify();
01986      }
01987      // Capa already in the list. Check if we need to update it.
01988      else if (cap->getValue()->GetType() == CAPA_INVERTER_INPUT_DC_CURRENT_TYPE) {
01989           CValue<float> *val = (CValue<float>*) cap->getValue();
01990 
01991           if (val -> Get() != f) {
01992                val->Set(f);
01993                cap->Notify();
01994           }
01995      } else {
01996           LOGDEBUG(logger, "BUG: " << CAPA_INVERTER_INPUT_DC_CURRENT_NAME
01997                     << " not a float");
01998      }
01999 
02000      return true;
02001 }
02002 
02003 bool CInverterSputnikSSeries::token_IL1(const vector<string> & tokens)
02004 {
02005      // unit: 0.01 Amps
02006      if (tokens.size() != 2)
02007           return false;
02008 
02009      unsigned int raw;
02010      float f;
02011      sscanf(tokens[1].c_str(), "%x", &raw);
02012 
02013      f = raw * 0.01;
02014      // lookup if we already know that information.
02015      CCapability *cap =
02016                GetConcreteCapability(CAPA_INVERTER_GRID_AC_CURRENT_NAME);
02017 
02018      if (!cap) {
02019           string s;
02020           IValue *v;
02021           CCapability *c;
02022           s = CAPA_INVERTER_GRID_AC_CURRENT_NAME;
02023           v = IValue::Factory(CAPA_INVERTER_GRID_AC_CURRENT_TYPE);
02024           ((CValue<float>*) v)->Set(f);
02025           c = new CCapability(s, v, this);
02026           AddCapability(s, c);
02027 
02028           // TODO: Check if we schould derefer (using a scheduled work) this.
02029           cap = GetConcreteCapability(CAPA_CAPAS_UPDATED);
02030           cap->Notify();
02031      }
02032      // Capa already in the list. Check if we need to update it.
02033      else if (cap->getValue()->GetType() == CAPA_INVERTER_GRID_AC_CURRENT_TYPE) {
02034           CValue<float> *val = (CValue<float>*) cap->getValue();
02035 
02036           if (val -> Get() != f) {
02037                val->Set(f);
02038                cap->Notify();
02039           }
02040      } else {
02041           LOGDEBUG(logger, "BUG: " << CAPA_INVERTER_GRID_AC_CURRENT_NAME
02042                     << " not a float");
02043      }
02044 
02045      return true;
02046 }
02047 
02048 bool CInverterSputnikSSeries::token_IL2(const vector<string> & tokens)
02049 {
02050      // FIXME TODO Implement me!
02051      return true;
02052 }
02053 
02054 bool CInverterSputnikSSeries::token_IL3(const vector<string> & tokens)
02055 {
02056      // FIXME TODO Implement me!
02057      return true;
02058 }
02059 
02060 bool CInverterSputnikSSeries::token_TKK(const vector<string> & tokens)
02061 {
02062      // Unit 1 °C
02063      if (tokens.size() != 2)
02064           return false;
02065 
02066      unsigned int raw;
02067      float f;
02068      sscanf(tokens[1].c_str(), "%x", &raw);
02069 
02070      f = raw;
02071      // lookup if we already know that information.
02072      CCapability *cap = GetConcreteCapability(CAPA_INVERTER_TEMPERATURE_NAME);
02073 
02074      if (!cap) {
02075           string s;
02076           IValue *v;
02077           CCapability *c;
02078           s = CAPA_INVERTER_TEMPERATURE_NAME;
02079           v = IValue::Factory(CAPA_INVERTER_TEMPERATURE_TYPE);
02080           ((CValue<float>*) v)->Set(f);
02081           c = new CCapability(s, v, this);
02082           AddCapability(s, c);
02083 
02084           // TODO: Check if we schould derefer (using a scheduled work) this.
02085           cap = GetConcreteCapability(CAPA_CAPAS_UPDATED);
02086           cap->Notify();
02087      }
02088      // Capa already in the list. Check if we need to update it.
02089      else if (cap->getValue()->GetType() == CAPA_INVERTER_TEMPERATURE_TYPE) {
02090           CValue<float> *val = (CValue<float>*) cap->getValue();
02091 
02092           if (val -> Get() != f) {
02093                val->Set(f);
02094                cap->Notify();
02095           }
02096      } else {
02097           LOGDEBUG(logger, "BUG: " << CAPA_INVERTER_TEMPERATURE_NAME
02098                     << " not a float");
02099      }
02100 
02101      return true;
02102 }
02103 
02104 bool CInverterSputnikSSeries::token_TK2(const vector<string> & tokens)
02105 {
02106      // FIXME TODO Implement me!
02107      return true;
02108 }
02109 
02110 bool CInverterSputnikSSeries::token_TK3(const vector<string> & tokens)
02111 {
02112      // FIXME TODO Implement me!
02113      return true;
02114 }
02115 
02116 bool CInverterSputnikSSeries::token_TMI(const vector<string> & tokens)
02117 {
02118      // FIXME TODO Implement me!
02119      return true;
02120 }
02121 
02122 bool CInverterSputnikSSeries::token_THR(const vector<string> & tokens)
02123 {
02124      // FIXME TODO Implement me!
02125      return true;
02126 }
02127 
02128 bool CInverterSputnikSSeries::token_SYS(const vector<string> &tokens)
02129 {
02130      // gets the system state of the inverter.
02131      // note: Alarms are handled with another command, SAL.
02132 
02133      // SYS reponses a code (eg. 20004) and a second parameter, which I
02134      // never saw != 0.
02135      if (tokens.size() != 3)
02136           return false;
02137 
02138      if (tokens[2] != "0") {
02139           LOGINFO(logger, "Received an unknown SYS response. Please file a bug"
02140                     << " along with the following: " << tokens[0] << ","
02141                     << tokens[1] << "," << tokens[2]);
02142      }
02143 
02144      unsigned int code;
02145      sscanf(tokens[1].c_str(), "%x", &code);
02146 
02147      string description;
02148 
02149      int i = 0;
02150      do {
02151           if (statuscodes[i].code == code)
02152                break;
02153      } while (statuscodes[++i].code != (unsigned int) -1);
02154 
02155      if (laststatuscode != (unsigned int) -1 && statuscodes[i].code
02156                == (unsigned int) -1) {
02157           LOGINFO(logger, "SYS reported an (too us) unknown status code of "
02158                     << tokens[0] << "=" << tokens[1] << "," << tokens[2]
02159           );
02160           LOGINFO (logger,
02161                     " PLEASE file a with all information you have, for example,"
02162                     << " reading the display of the inverter and of course the infors given above."
02163           );
02164      }
02165 
02166      laststatuscode = statuscodes[i].code;
02167 
02168      /* Update the status */
02169      CCapability *cap = GetConcreteCapability(CAPA_INVERTER_STATUS_NAME);
02170      if (!cap) {
02171           string s;
02172           IValue *v;
02173           CCapability *c;
02174           s = CAPA_INVERTER_STATUS_NAME;
02175           v = IValue::Factory(CAPA_INVERTER_STATUS_TYPE);
02176           ((CValue<int>*) v)->Set(statuscodes[i].status);
02177           c = new CCapability(s, v, this);
02178           AddCapability(s, c);
02179 
02180           // TODO: Check if we schould derefer (using a scheduled work) this.
02181           cap = GetConcreteCapability(CAPA_CAPAS_UPDATED);
02182           cap->Notify();
02183      } else if (cap->getValue()->GetType() == CAPA_INVERTER_STATUS_TYPE) {
02184           CValue<int> * val = (CValue<int> *) cap->getValue();
02185           if (val->Get() != statuscodes[i].status) {
02186                val->Set(statuscodes[i].status);
02187                cap->Notify();
02188           }
02189      } else {
02190           LOGDEBUG(logger, "BUG: " << CAPA_INVERTER_STATUS_NAME << " not a int");
02191      }
02192 
02193      // now also do the same with the string.
02194      cap = GetConcreteCapability(CAPA_INVERTER_STATUS_READABLE_NAME);
02195      if (!cap) {
02196           string s;
02197           IValue *v;
02198           CCapability *c;
02199           s = CAPA_INVERTER_STATUS_READABLE_NAME;
02200           v = IValue::Factory(CAPA_INVERTER_STATUS_READABLE_TYPE);
02201           ((CValue<string>*) v)->Set(statuscodes[i].description);
02202           c = new CCapability(s, v, this);
02203           AddCapability(s, c);
02204 
02205           // TODO: Check if we schould derefer (using a scheduled work) this.
02206           cap = GetConcreteCapability(CAPA_CAPAS_UPDATED);
02207           cap->Notify();
02208      } else if (cap->getValue()->GetType() == CAPA_INVERTER_STATUS_READABLE_TYPE) {
02209           CValue<string> * val = (CValue<string> *) cap->getValue();
02210           if (val->Get() != statuscodes[i].description) {
02211                val->Set(statuscodes[i].description);
02212                cap->Notify();
02213           }
02214      } else {
02215           LOGDEBUG(logger, "BUG: " << CAPA_INVERTER_STATUS_READABLE_NAME
02216                     << " not a string ");
02217      }
02218 
02219      return true;
02220 }
02221 
02230 void CInverterSputnikSSeries::create_versioncapa(void)
02231 {
02232      string ver;
02233      char buf[128];
02234 
02235      unsigned int major, minor;
02236 
02237      // Won't build a version string if the "major" version is unknown.
02238      if (!swversion)
02239           return;
02240 
02241      major = swversion / 10;
02242      minor = swversion % 10;
02243 
02244      if (swbuild) {
02245           sprintf(buf, "%d.%d Build %d", major, minor, swbuild);
02246      } else {
02247           sprintf(buf, "%d.%d", major, minor);
02248      }
02249 
02250      ver = buf;
02251 
02252      // lookup if we already know that information.
02253      CCapability *cap = GetConcreteCapability(CAPA_INVERTER_FIRMWARE);
02254 
02255      if (!cap) {
02256           string s;
02257           IValue *v;
02258           CCapability *c;
02259           s = CAPA_INVERTER_FIRMWARE;
02260           v = IValue::Factory(CAPA_INVERTER_FIRMWARE_TYPE);
02261           ((CValue<string>*) v)->Set(ver);
02262           c = new CCapability(s, v, this);
02263           AddCapability(s, c);
02264 
02265           // TODO: Check if we schould derefer (using a scheduled work) this.
02266           cap = GetConcreteCapability(CAPA_CAPAS_UPDATED);
02267           cap->Notify();
02268      } else if (cap->getValue()->GetType() == IValue::string_type) {
02269           CValue<string> *val = (CValue<string>*) cap->getValue();
02270           if (ver != val->Get()) {
02271                val->Set(ver);
02272                cap->Notify();
02273           }
02274      }
02275 }
02276 
02277 #endif