Main Page | Namespace List | Alphabetical List | Class List | Directories | File List | Class Members | File Members

DeviceManager.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   SpikeStream Simulation                                                *
00003  *   Copyright (C) 2007 by David Gamez                                     *
00004  *   david@davidgamez.eu                                                   *
00005  *   Version 0.1                                                           *
00006  *                                                                         *
00007  *   This program is free software; you can redistribute it and/or modify  *
00008  *   it under the terms of the GNU General Public License as published by  *
00009  *   the Free Software Foundation; either version 2 of the License, or     *
00010  *   (at your option) any later version.                                   *
00011  *                                                                         *
00012  *   This program is distributed in the hope that it will be useful,       *
00013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00015  *   GNU General Public License for more details.                          *
00016  *                                                                         *
00017  *   You should have received a copy of the GNU General Public License     *
00018  *   along with this program; if not, write to the                         *
00019  *   Free Software Foundation, Inc.,                                       *
00020  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
00021  ***************************************************************************/
00022 
00023 //SpikeStream includes
00024 #include "DeviceManager.h"
00025 #include "Debug.h"
00026 #include "Utilities.h"
00027 #include "DeviceTypes.h"
00028 #include "SimulationClock.h"
00029 #include "SpikeStreamSimulation.h"
00030 
00031 //Other includes
00032 #include <math.h>
00033 #include <errno.h>
00034 #include <sys/shm.h>
00035 #include <iostream>
00036 #include <mysql++.h>
00037 using namespace mysqlpp;
00038 using namespace std;
00039 
00040 //#define DEVICE_FIRING_MODE_DEBUG
00041 //#define DEVICE_DEBUG
00042 
00043 /*! Main constructor. */
00044 DeviceManager::DeviceManager(unsigned int devID, unsigned int neurGrpID, double devFiringMode, DBInterface* devDBInter, DBInterface *netDBInter){
00045         //Store database references
00046         deviceDBInterface = devDBInter;
00047         networkDBInterface = netDBInter;
00048 
00049         //Store information about device and neuron group
00050         deviceID = devID;
00051         neuronGrpID = neurGrpID;
00052 
00053         //Initialise variables
00054         deviceType = UNKNOWN_DEVICE_VALUE;
00055         deviceOpen = false;
00056 
00057         //Initialise device buffers
00058         for(int i=0; i<NUMBER_OF_DELAY_VALUES; ++i){
00059                 deviceBuffer[i].set_empty_key(EMPTY_NEURON_ID_KEY);
00060                 deviceBuffer[i].set_deleted_key(DELETED_NEURON_ID_KEY);
00061         }
00062 
00063         //Load up information about this device
00064         try{
00065                 loadDeviceInformation();
00066         }
00067         catch (const BadQuery& er) {// Handle any query errors
00068                 ostringstream errorStrStream;
00069                 errorStrStream<<"Bad query when loading device information: \""<<er.what()<<"\"";
00070                 SpikeStreamSimulation::systemError(errorStrStream.str());
00071         }
00072         catch (const Exception& er) {// Catch-all for any other MySQL++ exceptions
00073                 ostringstream errorStrStream;
00074                 errorStrStream<<"Exception thrown loading device information: \""<<er.what()<<"\"";
00075                 SpikeStreamSimulation::systemError(errorStrStream.str());
00076         }
00077         catch(std::exception& er){// Catch-all for any other exceptions
00078                 ostringstream errorStrStream;
00079                 errorStrStream<<"Exception thrown loading device information: \""<<er.what()<<"\"";
00080                 SpikeStreamSimulation::systemError(errorStrStream.str());
00081         }
00082 
00083 
00084         //Extract information about the firing mode - deviceType should be initialised at this point
00085         if(DeviceTypes::isInputDevice(deviceType)){
00086                 if(devFiringMode == INPUT_DIRECT_FIRING_MODE){
00087                         directFiringMode = true;
00088                         synapticWeight = 0.0;
00089                         #ifdef DEVICE_FIRING_MODE_DEBUG
00090                                 cout<<"DEVICE FIRING MODE: Input device. Direct firing mode"<<endl;
00091                         #endif//DEVICE_FIRING_MODE_DEBUG
00092                 }
00093                 else if (devFiringMode == OUTPUT_FIRING_MODE){
00094                         SpikeStreamSimulation::systemError("Device firing mode is set to output device, but this is an input device!");
00095                 }
00096                 else{
00097                         directFiringMode = false;
00098                         synapticWeight = devFiringMode;
00099                         #ifdef DEVICE_FIRING_MODE_DEBUG
00100                                 cout<<"DEVICE FIRING MODE: Input device. Synaptic firing mode with weight = "<<synapticWeight<<endl;
00101                         #endif//DEVICE_FIRING_MODE_DEBUG
00102                 }
00103         }
00104         else{//Should not be using these if it is an output device.
00105                 directFiringMode = false;
00106                 synapticWeight = 0.0;
00107                 #ifdef DEVICE_FIRING_MODE_DEBUG
00108                         cout<<"DEVICE FIRING MODE: Output device. No firing mode set"<<endl;
00109                 #endif//DEVICE_FIRING_MODE_DEBUG
00110         }
00111 
00112         //Remove all entries from the synchronization delay database
00113         clearSynchronizationDelay();
00114 }
00115 
00116 
00117 /*! Default constructor. Used when the device manager will not be used but might 
00118         have methods called on it. */
00119 // FIXME ELIMINATE THIS AT SOME POINT
00120 DeviceManager::DeviceManager(){
00121         //Initialise variables
00122         deviceDBInterface = NULL;
00123         deviceID = 0;
00124         neuronGrpID = 0;
00125         deviceType = UNKNOWN_DEVICE_VALUE;
00126         deviceOpen = false;
00127 }
00128 
00129 
00130 /*! Destructor. */
00131 DeviceManager::~DeviceManager(){
00132         #ifdef MEMORY_DEBUG
00133                 cout<<"DESTROYING DEVICE MANAGER"<<endl;
00134         #endif//MEMORY_DEBUG    
00135 
00136         if(deviceOpen)
00137                 closeDevice();
00138 
00139         /*Remove all entries from the synchronization delay database
00140                 Only do this if it is a meaningful device. Otherwise dBInterface may not exist */
00141         if(deviceDBInterface != NULL)
00142                 clearSynchronizationDelay();
00143 }
00144 
00145 
00146 //-------------------------------------------------------------------------
00147 //-------------------------- PUBLIC METHODS -------------------------------
00148 //-------------------------------------------------------------------------
00149 
00150 /*! Does anything else that is needed to reset device. Returns true if device
00151         closes successfully. */
00152 bool DeviceManager::closeDevice(){
00153         if(deviceOpen){
00154                 //Thread listening on socket for incoming messages
00155                 if(deviceType == DeviceTypes::syncUDPNetworkInput){
00156                         if(udpSyncClient->closeDevice()){
00157                                 deviceOpen = false;
00158                                 return true;
00159                         }
00160                 }
00161                 else if (deviceType == DeviceTypes::syncUDPNetworkOutput){
00162                         if(udpSyncServer->closeDevice()){
00163                                 deviceOpen = false;
00164                                 return true;
00165                         }
00166                 }
00167                 else if(deviceType == DeviceTypes::syncTCPNetworkInput || deviceType == DeviceTypes::syncTCPNetworkVisionInput){
00168                         if(tcpSyncClient->closeDevice()){
00169                                 deviceOpen = false;
00170                                 return true;
00171                         }
00172                 }
00173                 else if(deviceType == DeviceTypes::syncTCPNetworkOutput){
00174                         if(tcpSyncServer->closeDevice()){
00175                                 deviceOpen = false;
00176                                 return true;
00177                         }
00178                 }
00179                 else{
00180                         SpikeStreamSimulation::systemError("DeviceManager: ATTEMPTING TO CLOSE AN UNRECOGNIZED DEVICE TYPE");
00181                         return false;
00182                 }
00183         }
00184         return true;
00185 }
00186 
00187 
00188 /*! Instructs the device to download data from the external device or if data is being 
00189         obtained asynchronously it returns true if the thread is still running to do this. */
00190 bool DeviceManager::fetchData(){
00191         if(deviceType == DeviceTypes::syncTCPNetworkInput || deviceType == DeviceTypes::syncTCPNetworkVisionInput){
00192                 return tcpSyncClient->fetchData();//Get data from external device
00193         }
00194         else if (deviceType == DeviceTypes::syncUDPNetworkInput){
00195                 return udpSyncClient->clientThreadRunning();//Check client thread is still running
00196         }
00197         else{
00198                 SpikeStreamSimulation::systemError("DeviceManager: ATTEMPTING TO RECEIVE DATA FROM AN UNRECOGNIZED DEVICE TYPE");
00199                 return false;
00200         }
00201 }
00202 
00203 
00204 /*! Returns the type of this device. */
00205 unsigned int DeviceManager::getDeviceType(){
00206         return deviceType;
00207 }
00208 
00209 
00210 /*! When synchronizing to an external device this returns the time
00211         between time steps of the external device. */
00212 unsigned int DeviceManager::getExternalComputeTime_us(){
00213         if(deviceType == DeviceTypes::syncUDPNetworkInput)
00214                 return udpSyncClient->getExternalComputeTime_us();
00215         else{
00216                 SpikeStreamSimulation::systemError("DeviceManager: CALLING GET EXTERNAL COMPUTE TIME WHEN IT IS NOT APPROPRIATE");
00217                 return 0;
00218         }
00219 }
00220 
00221 
00222 /*! When synchronizing to an external device, this returns whether the external
00223         device is delaying or not. */
00224 bool DeviceManager::getExternalSyncDelay(){
00225         if(deviceType == DeviceTypes::syncUDPNetworkInput)
00226                 return udpSyncClient->getExternalSyncDelay();
00227         else{
00228                 SpikeStreamSimulation::systemError("DeviceManager: CALLING EXTERNAL SYNC DELAY WHEN IT IS NOT APPROPRIATE");
00229                 return false;
00230         }
00231 }
00232 
00233 
00234 /*! Returns true if this is an input device - i.e. provides data TO SpikeStream
00235         Uses the same method as DeviceTypes method to determine this. */
00236 bool DeviceManager::isInputDevice(){
00237         if(deviceType % 2 == 1)
00238                 return true;
00239         return false;
00240 }
00241 
00242 
00243 /*! Returns true if this is an output device - i.e. receives data FROM SpikeStream
00244         Uses the same method as DeviceTypes method to determine this. */
00245 bool DeviceManager::isOutputDevice(){
00246         if(deviceType % 2 == 0)
00247                 return true;
00248         return false;
00249 }
00250 
00251 
00252 /*! Sets the neuron array for this device manager. */
00253 void DeviceManager::setNeuronArray(Neuron **neurArr){
00254         neuronArray = neurArr;
00255 }
00256 
00257 
00258 /*! Sets a reference to the map used to record which neurons have been changed during
00259         the timestep. */
00260 void DeviceManager::setNeuronUpdateMap(dense_hash_map<unsigned int, bool, hash<unsigned int> >* neurUdtMp){
00261         neuronUpdateMap = neurUdtMp;
00262 }
00263 
00264 
00265 /*! Passes a reference to the vector containing the currently firing neurons
00266         to the device, which passes it on to other classes as necessary. */
00267 void DeviceManager::setNeuronVector(vector<unsigned int> *neurVectPtr, unsigned int startNeurID){
00268         startNeuronID = startNeurID;
00269         if (deviceType == DeviceTypes::syncUDPNetworkOutput){
00270                 udpSyncServer->setNeuronVector(neurVectPtr, startNeurID);
00271         }
00272         else if(deviceType == DeviceTypes::syncTCPNetworkOutput){
00273                 tcpSyncServer->setNeuronVector(neurVectPtr, startNeurID);
00274         }
00275 }
00276 
00277 
00278 /*! Sends the data from SpikeStream to the external device. */
00279 bool DeviceManager::updateDevice(){
00280         /*Output spikes to network. Want to output the x and y values of the spike events
00281                 and the time of the events. */
00282         if (deviceType == DeviceTypes::syncUDPNetworkOutput){
00283                 return udpSyncServer->sendSpikeData();
00284         }
00285         else if (deviceType == DeviceTypes::syncTCPNetworkOutput){
00286                 return tcpSyncServer->sendSpikeData();
00287         }
00288         else{
00289                 SpikeStreamSimulation::systemError("DeviceManager: ATTEMPTING TO SEND DATA TO AN UNRECOGNIZED DEVICE TYPE");
00290                 return false;
00291         }
00292 }
00293 
00294 
00295 /*! Updates neurons with data from device. */
00296 void DeviceManager::updateNeurons(){
00297         //Extract the data and fill the device buffer
00298         fillDeviceBuffer();
00299 
00300         /* Fire all the neurons at the current position in the buffer, either directly or
00301                 by injecting the specified synaptic current. */
00302         for(dense_hash_map<unsigned int, bool, hash<unsigned int> >::iterator iter = deviceBuffer[bufferCounter].begin(); iter != deviceBuffer[bufferCounter].end(); ++iter){
00303                 if(directFiringMode)
00304                         neuronArray[iter->first]->fireNeuron();
00305                 else{
00306                         neuronArray[iter->first]->changePostSynapticPotential(synapticWeight, 0);
00307                         (*neuronUpdateMap)[ iter->first + startNeuronID ] = true;
00308                 }
00309         }
00310         
00311         //Reset currently active buffer and advance the buffer
00312         deviceBuffer[bufferCounter].clear();//Empty the buffer's vector
00313         ++bufferCounter;
00314         bufferCounter = bufferCounter % NUMBER_OF_DELAY_VALUES;
00315 }
00316 
00317 
00318 //-------------------------------------------------------------------------
00319 //------------------------- PRIVATE METHODS -------------------------------
00320 //-------------------------------------------------------------------------
00321 
00322 /*! Empties the SynchronizationDelay database. */
00323 void DeviceManager::clearSynchronizationDelay(){
00324         try{
00325                 Query deviceQuery = deviceDBInterface->getQuery();
00326                 deviceQuery.reset();
00327                 deviceQuery<<"DELETE FROM SynchronizationDelay";
00328                 deviceQuery.execute();
00329         }
00330         catch (const BadQuery& er) {// Handle any query errors
00331                 ostringstream errorStrStream;
00332                 errorStrStream<<"Bad query when clearing synchronization delay: \""<<er.what()<<"\"";
00333                 SpikeStreamSimulation::systemError(errorStrStream.str());
00334         }
00335         catch (const Exception& er) {// Catch-all for any other MySQL++ exceptions
00336                 ostringstream errorStrStream;
00337                 errorStrStream<<"Exception thrown clearing synchronization delay: \""<<er.what()<<"\"";
00338                 SpikeStreamSimulation::systemError(errorStrStream.str());
00339         }
00340 }
00341 
00342 
00343 /*! Fills up device buffer with data obtained from an external device. */
00344 void DeviceManager::fillDeviceBuffer(){
00345         if(deviceType == DeviceTypes::syncUDPNetworkInput){
00346                 //Lock the mutex so that the spike client cannot change threadSpikeBuffer whilst it is being loaded
00347                 udpSyncClient->lockMutex();
00348         
00349                 //Fill the device buffer
00350                 for(unsigned int i=0; i<udpSyncClient->spikeCount; ++i){
00351         
00352                         //Extract x, y and delay values
00353                         unsigned int xPos = udpSyncClient->spikeBuffer[i * 3];
00354                         unsigned int yPos = udpSyncClient->spikeBuffer[i * 3 + 1];
00355                         unsigned int delay = udpSyncClient->spikeBuffer[i * 3 + 2];
00356         
00357                         //Calculate position in array and add to device buffer
00358                         int arrayIndex = xPos + yPos*neuronGrpWidth;
00359                         int insertionPosition = (bufferCounter + delay) % NUMBER_OF_DELAY_VALUES;
00360                         deviceBuffer[insertionPosition][arrayIndex] = true;//Add array position to device buffer at delay after buffer counter
00361                 }
00362         
00363                 //Reset spike count
00364                 udpSyncClient->spikeCount = 0;
00365         
00366                 //Unlock the mutex so that spike client can continue to fill it
00367                 udpSyncClient->unlockMutex();
00368         }
00369         else if(deviceType == DeviceTypes::syncTCPNetworkInput || deviceType == DeviceTypes::syncTCPNetworkVisionInput){
00370                 /* NOTE Fetching of the data is done by SpikeStreamSimulation class
00371                         by calling fetchData() in this class */
00372                 for(unsigned int i=0; i<tcpSyncClient->spikeCount; ++i){
00373 
00374                         //Extract x, y and delay values
00375                         unsigned int xPos = tcpSyncClient->spikeBuffer[i * 3];
00376                         unsigned int yPos = tcpSyncClient->spikeBuffer[i * 3 + 1];
00377                         unsigned int delay = tcpSyncClient->spikeBuffer[i * 3 + 2];
00378 
00379                         //Calculate position in array and add to device buffer
00380                         int arrayIndex = xPos + yPos*neuronGrpWidth;
00381                         int insertionPosition = (bufferCounter + delay) % NUMBER_OF_DELAY_VALUES;
00382                         deviceBuffer[insertionPosition][arrayIndex] = true;//Add array position to device buffer at delay after buffer counter
00383                 }
00384         }
00385         else{
00386                 SpikeStreamSimulation::systemError("DeviceManager: DEVICE TYPE NOT RECOGNIZED");
00387         }
00388 }
00389 
00390 
00391 /*! Loads up information about the device that this class has to manage.
00392         Should only be called once when device manager initialises. 
00393         NOTE: Exceptions should be handled by the calling class. */
00394 void DeviceManager::loadDeviceInformation(){
00395         deviceOpen = false;
00396 
00397         #ifdef DEVICE_DEBUG
00398                 cout<<"DeviceManager: Loading Device information"<<endl;
00399         #endif//DEVICE_DEBUG
00400 
00401         /* Get the width and length of the neuron group. */
00402         Query networkQuery = networkDBInterface->getQuery();
00403         networkQuery.reset();
00404         networkQuery<<"SELECT Width, Length FROM NeuronGroups WHERE NeuronGrpID = "<<neuronGrpID;
00405         Result networkResults = networkQuery.store();
00406         Row networkRow(*networkResults.begin());
00407         neuronGrpWidth = Utilities::getUInt((std::string)networkRow["Width"]);
00408         neuronGrpLength = Utilities::getUInt((std::string)networkRow["Length"]);
00409 
00410         //Get information about the device
00411         Query deviceQuery = deviceDBInterface->getQuery();
00412         deviceQuery.reset();
00413         deviceQuery<<"SELECT IPAddress, Port, TotalNumColumns, TotalNumRows, Type FROM Devices WHERE DeviceID = "<<deviceID;
00414         Result deviceResults = deviceQuery.store();
00415         Row deviceRow(*deviceResults.begin());
00416         deviceType = Utilities::getUInt((std::string)deviceRow["Type"]);
00417         unsigned int deviceWidth = Utilities::getUInt((std::string)deviceRow["TotalNumColumns"]);
00418         unsigned int deviceLength = Utilities::getUInt((std::string)deviceRow["TotalNumRows"]);
00419         unsigned int devicePort = Utilities::getUInt((std::string)deviceRow["Port"]);
00420 
00421         #ifdef DEVICE_DEBUG
00422                 cout<<"Creating device with type="<<deviceType<<"; IPAddress="<<deviceRow["IPAddress"]<<"; Port="<<devicePort<<"; deviceWidth="<<deviceWidth<<"; deviceLength="<<deviceLength<<endl;
00423         #endif//DEVICE_DEBUG
00424 
00425         //Open up appropriate input or output port
00426         if(deviceType == DeviceTypes::syncUDPNetworkInput){//Listen for incoming spikes from network and adjusts its time to match that of the external source
00427                 //Create a client to listen for synchronized udp input
00428                 udpSyncClient = new UDPSynchronizedClient(neuronGrpWidth, neuronGrpLength);
00429 
00430                 //Open the socket on the client and start thread running
00431                 if(udpSyncClient->openSocket((std::string)deviceRow["IPAddress"], devicePort))
00432                         udpSyncClient->start();
00433                 else
00434                         return;
00435         }
00436         else if(deviceType == DeviceTypes::syncUDPNetworkOutput){
00437                 udpSyncServer = new UDPSynchronizedServer(deviceDBInterface, neuronGrpWidth);
00438                 if(!udpSyncServer->openSocket((std::string)deviceRow["IPAddress"], devicePort))
00439                         return;
00440         }
00441         else if(deviceType == DeviceTypes::syncTCPNetworkInput || deviceType == DeviceTypes::syncTCPNetworkVisionInput){
00442                 //Create client to request data from device
00443                 tcpSyncClient = new TCPSynchronizedClient(neuronGrpWidth, neuronGrpLength);
00444 
00445                 /* If it is a vision input set device to process incoming data as two byte
00446                         coordinates */
00447                 if(deviceType == DeviceTypes::syncTCPNetworkVisionInput){
00448                         tcpSyncClient->setTwoByteCoords(true);
00449                 }
00450                 
00451                 //Open socket
00452                 if(!tcpSyncClient->openSocket((std::string)deviceRow["IPAddress"], devicePort))
00453                         return;
00454         }
00455         else if(deviceType == DeviceTypes::syncTCPNetworkOutput){
00456                 tcpSyncServer = new TCPSynchronizedServer(neuronGrpWidth);
00457                 if(!tcpSyncServer->openSocket((std::string)deviceRow["IPAddress"], devicePort))
00458                         return;
00459         }
00460         else {
00461                 SpikeStreamSimulation::systemError("DeviceManager: DEVICE TYPE NOT RECOGNIZED");
00462         }
00463 
00464         //Sort out whether the input should be rotated or not
00465         if(neuronGrpWidth == deviceWidth && neuronGrpLength == deviceLength){
00466                 rotatePattern = false;//FIXME ROTATE PATTERN NOT IMPLEMENTED
00467         } 
00468         else if(neuronGrpWidth == deviceLength && neuronGrpLength == deviceWidth){
00469                 rotatePattern = true;//FIXME ROTATE PATTERN NOT IMPLEMENTED
00470                 SpikeStreamSimulation::systemError("DeviceManager: ROTATE PATTERN NOT IMPLEMENTED.\nDEVICE WIDTH AND LENGTH DO NOT MATCH NEURON GROUP WIDTH AND LENGTH");
00471                 return;
00472         }
00473         else{
00474                 SpikeStreamSimulation::systemError("DeviceManager: DEVICE WIDTH AND LENGTH DO NOT MATCH NEURON GROUP WIDTH AND LENGTH");
00475                 return;
00476         }
00477 
00478         //Device is now open
00479         deviceOpen = true;
00480 
00481         #ifdef DEVICE_DEBUG
00482                 cout<<"DeviceManager: Device information loaded"<<endl;
00483         #endif//DEVICE_DEBUG
00484 }
00485 
00486 

Generated on Mon Sep 3 22:24:34 2007 for SpikeStream Simulation by  doxygen 1.4.4