m_desktop.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       m_desktop.cc
00003 ///             Mode class for the Desktop mode
00004 ///
00005 
00006 /*
00007     Copyright (C) 2005-2010, Net Direct Inc. (http://www.netdirect.ca/)
00008 
00009     This program is free software; you can redistribute it and/or modify
00010     it under the terms of the GNU General Public License as published by
00011     the Free Software Foundation; either version 2 of the License, or
00012     (at your option) any later version.
00013 
00014     This program is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00017 
00018     See the GNU General Public License in the COPYING file at the
00019     root directory of this project for more details.
00020 */
00021 
00022 #include "m_desktop.h"
00023 #include "data.h"
00024 #include "protocol.h"
00025 #include "protostructs.h"
00026 #include "packet.h"
00027 #include "endian.h"
00028 #include "error.h"
00029 #include "usbwrap.h"
00030 #include "controller.h"
00031 #include <stdexcept>
00032 #include <sstream>
00033 
00034 #include "debug.h"
00035 
00036 namespace Barry { namespace Mode {
00037 
00038 
00039 ///////////////////////////////////////////////////////////////////////////////
00040 // Desktop Mode class
00041 
00042 Desktop::Desktop(Controller &con)
00043         : Mode(con, Controller::Desktop)
00044         , m_ic(0)
00045 {
00046 }
00047 
00048 Desktop::Desktop(Controller &con, const IConverter &ic)
00049         : Mode(con, Controller::Desktop)
00050         , m_ic(&ic)
00051 {
00052 }
00053 
00054 Desktop::~Desktop()
00055 {
00056 }
00057 
00058 ///////////////////////////////////////////////////////////////////////////////
00059 // protected members
00060 
00061 void Desktop::LoadCommandTable()
00062 {
00063         char rawCommand[] = { 6, 0, 0x0a, 0, 0x40, 0, 0, 1, 0, 0 };
00064         *((uint16_t*) rawCommand) = htobs(m_socket->GetSocket());
00065 
00066         Data command(rawCommand, sizeof(rawCommand));
00067         Data response;
00068 
00069         try {
00070                 m_socket->Packet(command, response);
00071 
00072                 MAKE_PACKET(rpack, response);
00073                 while( rpack->command != SB_COMMAND_DB_DONE ) {
00074                         m_socket->NextRecord(response);
00075 
00076                         rpack = (const Protocol::Packet *) response.GetData();
00077                         if( rpack->command == SB_COMMAND_DB_DATA && btohs(rpack->size) > 10 ) {
00078                                 // second packet is generally large, and contains
00079                                 // the command table
00080                                 m_commandTable.Clear();
00081                                 m_commandTable.Parse(response, 6);
00082                         }
00083                 }
00084 
00085                 ddout(m_commandTable);
00086 
00087         }
00088         catch( Usb::Error & ) {
00089                 eout("Desktop: error getting command table");
00090                 eeout(command, response);
00091                 throw;
00092         }
00093 }
00094 
00095 void Desktop::LoadDBDB()
00096 {
00097         Data command, response;
00098         DBPacket packet(*this, command, response);
00099         packet.GetDBDB();
00100 
00101         m_socket->Packet(packet);
00102 
00103         while( packet.Command() != SB_COMMAND_DB_DONE ) {
00104                 if( packet.Command() == SB_COMMAND_DB_DATA ) {
00105                         m_dbdb.Clear();
00106                         m_dbdb.Parse(response);
00107                 }
00108 
00109                 // advance!
00110                 m_socket->NextRecord(response);
00111         }
00112 }
00113 
00114 void Desktop::OnOpen()
00115 {
00116         // get command table and database database
00117         LoadCommandTable();
00118         LoadDBDB();
00119 }
00120 
00121 ///////////////////////////////////////////////////////////////////////////////
00122 // public API
00123 
00124 //
00125 // GetDBID
00126 //
00127 /// Get numeric database ID by name.
00128 ///
00129 /// \param[in]  name            Name of database, which matches one of the
00130 ///                             names listed in GetDBDB()
00131 ///
00132 /// \exception  Barry::Error
00133 ///             Thrown if name not found.
00134 ///
00135 unsigned int Desktop::GetDBID(const std::string &name) const
00136 {
00137         unsigned int ID = 0;
00138         // FIXME - this needs a better error handler...
00139         if( !m_dbdb.GetDBNumber(name, ID) ) {
00140                 throw Error("Desktop: database name not found: " + name);
00141         }
00142         return ID;
00143 }
00144 
00145 //
00146 // GetDBCommand
00147 //
00148 /// Get database command from command table.  Must call Open()
00149 /// before this.
00150 ///
00151 unsigned int Desktop::GetDBCommand(CommandType ct)
00152 {
00153         unsigned int cmd = 0;
00154         const char *cmdName = "Unknown";
00155 
00156         switch( ct )
00157         {
00158         case DatabaseAccess:
00159                 cmdName = "Database Access";
00160                 cmd = m_commandTable.GetCommand(cmdName);
00161                 break;
00162         default:
00163                 throw std::logic_error("Desktop: unknown command type");
00164         }
00165 
00166         if( cmd == 0 ) {
00167                 std::ostringstream oss;
00168                 oss << "Desktop: unable to get command code: " << cmdName;
00169                 throw Error(oss.str());
00170         }
00171 
00172         return cmd;
00173 }
00174 
00175 void Desktop::SetIConverter(const IConverter &ic)
00176 {
00177         m_ic = &ic;
00178 }
00179 
00180 //
00181 // GetRecordStateTable
00182 //
00183 /// Retrieve the record state table from the handheld device, using the given
00184 /// database ID.  Results will be stored in result, which will be cleared
00185 /// before adding.
00186 ///
00187 void Desktop::GetRecordStateTable(unsigned int dbId, RecordStateTable &result)
00188 {
00189         dout("Database ID: " << dbId);
00190 
00191         // start fresh
00192         result.Clear();
00193 
00194         Data command, response;
00195         DBPacket packet(*this, command, response);
00196         packet.GetRecordStateTable(dbId);
00197 
00198         m_socket->Packet(packet);
00199         result.Parse(response);
00200 
00201         // flush the command sequence
00202         while( packet.Command() != SB_COMMAND_DB_DONE )
00203                 m_socket->NextRecord(response);
00204 }
00205 
00206 //
00207 // AddRecord
00208 //
00209 /// Adds a record to the specified database.  RecordId is
00210 /// retrieved from build, and duplicate IDs are allowed by the device
00211 /// (i.e. you can have two records with the same ID)
00212 /// but *not* recommended!
00213 //
00214 void Desktop::AddRecord(unsigned int dbId, Builder &build)
00215 {
00216         dout("Database ID: " << dbId);
00217 
00218         Data command, response;
00219         DBPacket packet(*this, command, response);
00220 
00221         if( packet.SetRecord(dbId, build, m_ic) ) {
00222 
00223                 std::ostringstream oss;
00224 
00225                 m_socket->Packet(packet);
00226 
00227                 // successful packet transfer, so check the network return code
00228                 if( packet.Command() != SB_COMMAND_DB_DONE ) {
00229                         oss << "Desktop: device responded with unexpected packet command code: "
00230                             << "0x" << std::hex << packet.Command();
00231                         throw Error(oss.str());
00232                 }
00233 
00234                 if( packet.ReturnCode() != 0 ) {
00235                         oss << "Desktop: device responded with error code (command: "
00236                             << packet.Command() << ", code: "
00237                             << packet.ReturnCode() << ")";
00238                         throw Error(oss.str());
00239                 }
00240         }
00241 }
00242 
00243 //
00244 // GetRecord
00245 //
00246 /// Retrieves a specific record from the specified database.
00247 /// The stateTableIndex comes from the GetRecordStateTable()
00248 /// function.  GetRecord() does not clear the dirty flag.
00249 ///
00250 void Desktop::GetRecord(unsigned int dbId,
00251                            unsigned int stateTableIndex,
00252                            Parser &parser)
00253 {
00254         dout("Database ID: " << dbId);
00255 
00256         Data command, response;
00257         DBPacket packet(*this, command, response);
00258         packet.GetRecordByIndex(dbId, stateTableIndex);
00259 
00260         m_socket->Packet(packet);
00261 
00262         // perform copious packet checks
00263         if( response.GetSize() < SB_PACKET_RESPONSE_HEADER_SIZE ) {
00264                 eeout(command, response);
00265 
00266                 std::ostringstream oss;
00267                 oss << "Desktop: invalid response packet size of "
00268                     << std::dec << response.GetSize();
00269                 eout(oss.str());
00270                 throw Error(oss.str());
00271         }
00272         if( packet.Command() != SB_COMMAND_DB_DATA ) {
00273                 eeout(command, response);
00274 
00275                 std::ostringstream oss;
00276                 oss << "Desktop: unexpected command of 0x"
00277                     << std::setbase(16) << packet.Command()
00278                     << " instead of expected 0x"
00279                     << std::setbase(16) << (unsigned int)SB_COMMAND_DB_DATA;
00280                 eout(oss.str());
00281                 throw Error(oss.str());
00282         }
00283 
00284         // grab that data
00285         packet.Parse(parser, m_ic);
00286 
00287         // flush the command sequence
00288         while( packet.Command() != SB_COMMAND_DB_DONE )
00289                 m_socket->NextRecord(response);
00290 }
00291 
00292 //
00293 // SetRecord
00294 //
00295 /// Overwrites a specific record in the device as identified by the
00296 /// stateTableIndex.
00297 ///
00298 void Desktop::SetRecord(unsigned int dbId, unsigned int stateTableIndex,
00299                            Builder &build)
00300 {
00301         dout("Database ID: " << dbId << " Index: " << stateTableIndex);
00302 
00303         Data command, response;
00304         DBPacket packet(*this, command, response);
00305 
00306         // loop until builder object has no more data
00307         if( !packet.SetRecordByIndex(dbId, stateTableIndex, build, m_ic) ) {
00308                 throw std::logic_error("Desktop: no data available in SetRecord");
00309         }
00310 
00311         m_socket->Packet(packet);
00312 
00313         std::ostringstream oss;
00314 
00315         // successful packet transfer, so check the network return code
00316         if( packet.Command() != SB_COMMAND_DB_DONE ) {
00317                 oss << "Desktop: device responded with unexpected packet command code: "
00318                     << "0x" << std::hex << packet.Command();
00319                 throw Error(oss.str());
00320         }
00321 
00322         if( packet.ReturnCode() != 0 ) {
00323                 oss << "Desktop: device responded with error code (command: "
00324                     << packet.Command() << ", code: "
00325                     << packet.ReturnCode() << ")";
00326                 throw Error(oss.str());
00327         }
00328 }
00329 
00330 //
00331 // ClearDirty
00332 //
00333 /// Clears the dirty flag on the specified record in the specified database.
00334 ///
00335 void Desktop::ClearDirty(unsigned int dbId, unsigned int stateTableIndex)
00336 {
00337         dout("Database ID: " << dbId);
00338 
00339         Data command, response;
00340         DBPacket packet(*this, command, response);
00341         packet.SetRecordFlags(dbId, stateTableIndex, 0);
00342 
00343         m_socket->Packet(packet);
00344 
00345         // flush the command sequence
00346         while( packet.Command() != SB_COMMAND_DB_DONE )
00347                 m_socket->NextRecord(response);
00348 }
00349 
00350 //
00351 // DeleteRecord
00352 //
00353 /// Deletes the specified record in the specified database.
00354 ///
00355 void Desktop::DeleteRecord(unsigned int dbId, unsigned int stateTableIndex)
00356 {
00357         dout("Database ID: " << dbId);
00358 
00359         Data command, response;
00360         DBPacket packet(*this, command, response);
00361         packet.DeleteRecordByIndex(dbId, stateTableIndex);
00362 
00363         m_socket->Packet(packet);
00364 
00365         // flush the command sequence
00366         while( packet.Command() != SB_COMMAND_DB_DONE )
00367                 m_socket->NextRecord(response);
00368 }
00369 
00370 //
00371 // LoadDatabase
00372 //
00373 /// Retrieve a database from the handheld device, using the given parser
00374 /// to parse the resulting data, and optionally store it.
00375 ///
00376 /// See the RecordParser<> template to create a parser object.  The
00377 /// RecordParser<> template allows custom storage based on the type of
00378 /// database record retrieved.  The database ID and the parser Record
00379 /// type must match.
00380 ///
00381 /// \param[in]  dbId            Database Database ID - use GetDBID()
00382 /// \param[out] parser          Parser object which parses the resulting
00383 ///                             protocol data, and optionally stores it in
00384 ///                             a custom fashion.  See the RecordParser<>
00385 ///                             template.
00386 ///
00387 /// \exception  Barry::Error
00388 ///             Thrown on protocol error.
00389 ///
00390 /// \exception  std::logic_error
00391 ///             Thrown if not in Desktop mode.
00392 ///
00393 void Desktop::LoadDatabase(unsigned int dbId, Parser &parser)
00394 {
00395         dout("Database ID: " << dbId);
00396 
00397         Data command, response;
00398         DBPacket packet(*this, command, response);
00399         packet.GetRecords(dbId);
00400 
00401         m_socket->Packet(packet);
00402 
00403         while( packet.Command() != SB_COMMAND_DB_DONE ) {
00404                 if( packet.Command() == SB_COMMAND_DB_DATA ) {
00405                         // this size is the old header size, since using
00406                         // old command above
00407                         packet.Parse(parser, m_ic);
00408                 }
00409 
00410                 // advance!
00411                 m_socket->NextRecord(response);
00412         }
00413 }
00414 
00415 void Desktop::ClearDatabase(unsigned int dbId)
00416 {
00417         dout("Database ID: " << dbId);
00418 
00419         Data command, response;
00420         DBPacket packet(*this, command, response);
00421         packet.ClearDatabase(dbId);
00422 
00423         // wait up to a minute here for old, slower devices with lots of data
00424         m_socket->Packet(packet, 60000);
00425         if( packet.ReturnCode() != 0 ) {
00426                 std::ostringstream oss;
00427                 oss << "Desktop: could not clear database: (command: "
00428                     << "0x" << std::hex << packet.Command() << ", code: "
00429                     << "0x" << std::hex << packet.ReturnCode() << ")";
00430                 throw Error(oss.str());
00431         }
00432 
00433         // check response to clear command was successful
00434         if( packet.Command() != SB_COMMAND_DB_DONE ) {
00435                 eeout(command, response);
00436                 throw Error("Desktop: error clearing database, bad response");
00437         }
00438 }
00439 
00440 void Desktop::SaveDatabase(unsigned int dbId, Builder &builder)
00441 {
00442         dout("Database ID: " << dbId);
00443 
00444         // Protocol note: so far in testing, this CLEAR_DATABASE operation is
00445         //                required, since every record sent via SET_RECORD
00446         //                is treated like a hypothetical "ADD_RECORD" (perhaps
00447         //                SET_RECORD should be renamed)... I don't know if
00448         //                there is a real SET_RECORD... all I know is from
00449         //                the Windows USB captures, which uses this same
00450         //                technique.
00451         ClearDatabase(dbId);
00452 
00453         Data command, response;
00454         DBPacket packet(*this, command, response);
00455 
00456         // loop until builder object has no more data
00457         bool first = true;
00458         while( packet.SetRecord(dbId, builder, m_ic) ) {
00459                 dout("Database ID: " << dbId);
00460 
00461                 m_socket->Packet(packet, first ? 60000 : -1);
00462                 first = false;
00463 
00464                 std::ostringstream oss;
00465                 // successful packet transfer, so check the network return code
00466                 if( packet.Command() != SB_COMMAND_DB_DONE ) {
00467                         oss << "Desktop: device responded with unexpected packet command code: "
00468                             << "0x" << std::hex << packet.Command();
00469                         throw Error(oss.str());
00470                 }
00471 
00472                 if( packet.ReturnCode() != 0 ) {
00473                         oss << "Desktop: device responded with error code (command: "
00474                             << packet.Command() << ", code: "
00475                             << packet.ReturnCode() << ")";
00476                         throw Error(oss.str());
00477                 }
00478         }
00479 }
00480 
00481 }} // namespace Barry::Mode
00482 

Generated on 29 Mar 2010 for Barry by  doxygen 1.6.1