root/vtcross/trunk/src/cognitive_engines/CognitiveEngine.cpp @ 231

Revision 231, 20.7 KB (checked in by bhilburn, 15 years ago)

Style fixes for the most part. Perhaps one or two memory leaks. Added a
bunch of TODOs.

RevLine 
[218]1/* Virginia Tech Cognitive Radio Open Source Systems
2 * Virginia Tech, 2009
3 *
4 * LICENSE INFORMATION GOES HERE
5 */
6
7/* DESCRIPTION OF FILE.
8 */
9
10
11#include <cstdlib>
12#include <cstring>
13#include <stdint.h>
[230]14#include <math.h>
[218]15
16#include "vtcross/common.h"
17#include "vtcross/components.h"
18#include "vtcross/containers.h"
19#include "vtcross/debug.h"
20#include "vtcross/error.h"
21#include "vtcross/socketcomm.h"
[228]22#include "vtcross/cbr.h"
[218]23
[231]24// TODO this is really bad; need to move to a proper cbr.h
[228]25#include "cbr.c"
[218]26
[228]27#include <sqlite3.h>
28#include <sqlite3ext.h>
29
30
31static cbr myCBR;
32
[231]33
[218]34CognitiveEngine::CognitiveEngine()
35{
36    LOG("Creating Cognitive Engine.\n");
37    SML_present = false;
38    commandSocketFD = -1;
39}
40
41
42CognitiveEngine::~CognitiveEngine()
43{
[231]44    cbr_free(myCBR);
[218]45    delete [] pList;
46    delete [] oList;
47    delete [] uList;
48    delete [] radioInfo;
49}
50
51
[231]52CognitiveEngine::CognitiveEngine(const char *serverName, const char *serverPort, \
[218]53        const bool SML)
54{
55    LOG("Creating Cognitive Engine.\n");
56
57    pList = new Parameter[10];
58    oList = new Observable[10];
59    uList = new Utility[10];
60    radioInfo = new Radio_Info;
61
62    ConnectToRemoteComponent(serverName, serverPort, SML);
63}
64
65
66void
67CognitiveEngine::SendComponentType()
68{
69    SendMessage(commandSocketFD, "response_engine_cognitive");
70    LOG("Cognitive Engine responded to GetRemoteComponentType query.\n");
71}
72
73
74void
[231]75CognitiveEngine::ConnectToRemoteComponent(const char *serverName, \
76        const char *serverPort, const bool SML)
[218]77{
78    commandSocketFD = ClientSocket(serverName, serverPort);
79
80    SML_present = SML;
81
82    if(SML) {
83        RegisterComponent();
84        RegisterServices();
85        LOG("Cognitive Engine connected to SML at %s.\n", serverName);
86        ReceiveRadioConfiguration();
87        ReceiveExperience();
88    }
89    else {
90        RegisterComponent();
91        LOG("Cognitive Engine connected to shell at %s.\n", serverName);
92        ReceiveRadioConfiguration();
93        ReceiveExperience();
94    }
95}
96
97void
[230]98CognitiveEngine::ReceiveFeedback(Observable *observables, Parameter *parameters)
99{
100   LOG("Cognitive Engine:: Receiving feedback.\n");
[231]101   
102    uint32_t numberColumns =
103        radioInfo->numParameters +
104        radioInfo->numUtilities;
[230]105
[231]106    uint32_t obsColumns = radioInfo->numObservables + 1;
[230]107
[231]108    float valList[numberColumns];
109    float obsVals[numberColumns];
[230]110    char *nameList[numberColumns];
111    char *obsList[obsColumns];
112
113    size_t columnObsIndex = 0;
114    for (size_t i = 0; i < radioInfo->numObservables; i++){
[231]115        obsList[columnObsIndex] = (char*)observables[i].name.c_str();
[230]116        columnObsIndex++;
117    } 
118    obsList[columnObsIndex] = "utility";
119
120    size_t columnIndex = 0;
121    for (size_t i = 0; i < radioInfo->numParameters; i++){
[231]122        nameList[columnIndex] = (char*)parameters[i].name.c_str();
[230]123        columnIndex++;
124    }   
125    for (size_t i = 0; i < radioInfo->numUtilities; i++){
[231]126        nameList[columnIndex] = (char*)uList[i].name.c_str();
[230]127        columnIndex++;
128    }   
129
[231]130    size_t obsValueIndex = 0;
[230]131    for(size_t i = 0; i < radioInfo->numObservables; i++) {
[231]132        obsVals[obsValueIndex] = observables[i].value;
133        obsValueIndex++;
134    }
[230]135
[231]136    /* Calculate Utility */
137    float newUtilityValue = 0;
138
[230]139    for(size_t i = 0; i < radioInfo->numUtilities; i++) {
[231]140        newUtilityValue = newUtilityValue + (uList[i].target - observables[i].value);
141    }
142    obsVals[obsValueIndex] = newUtilityValue;
[230]143
[231]144    size_t returnValueIndex = 0;
[230]145    for(size_t i = 0; i < radioInfo->numParameters; i++) {
[231]146        valList[returnValueIndex] = parameters[i].value;
147        returnValueIndex++;
148    }
[230]149    for(size_t i = 0; i < radioInfo->numUtilities; i++) {
[231]150        valList[returnValueIndex] = uList[i].target;
151        returnValueIndex++;
152    }
[230]153
[231]154    cbr_update(myCBR, nameList, obsList, valList, obsVals,
155            numberColumns, obsColumns);
[230]156}
157
158
159void
[218]160CognitiveEngine::WaitForSignal()
161{
162    char buffer[256];
163
164    while(true) {
165        memset(buffer, 0, 256);
166       
167        ReadMessage(commandSocketFD, buffer);
168
169        // TODO this is ugly... is there a better way? Doesn't strcmp compare the
170        // whole string?  We only need to compare until we find a single different
171        // byte...
172        //
173        // If we send integer op codes rather than strings, this process will be
174        // MUCH faster since instead of donig string compares we can simply
175        // switch on the integer value...
[230]176        if(strcmp(buffer, "update_performance") == 0) {
[222]177           
[230]178            /* Receive Set of current Parameters */
179            memset(buffer, 0, 256);
[231]180            ReadMessage(commandSocketFD, buffer);
[230]181            uint32_t numParameters = atoi(buffer);
182   
183            Parameter *p = new Parameter[numParameters];
184 
185            for(size_t i = 0; i < numParameters; i++) {
186                memset(buffer, 0, 256);
187                ReadMessage(commandSocketFD, buffer);
188                p[i].name = std::string(buffer);
189   
190                memset(buffer, 0, 256);
191                ReadMessage(commandSocketFD, buffer);
192                p[i].value = atof(buffer);
193            }
194
[222]195            /* Receive Set of Observables */
[230]196            memset(buffer, 0, 256);
[231]197            ReadMessage(commandSocketFD, buffer);
[230]198            uint32_t numObservables = atoi(buffer);
199   
200            Observable *o = new Observable[numObservables];
201 
202            for(size_t i = 0; i < numObservables; i++) {
203                memset(buffer, 0, 256);
204                ReadMessage(commandSocketFD, buffer);
205                o[i].name = std::string(buffer);
206   
207                memset(buffer, 0, 256);
208                ReadMessage(commandSocketFD, buffer);
209                o[i].value = atof(buffer);
210            } 
[218]211
[231]212            ReceiveFeedback(o,p);
[230]213
214            delete [] o;
215            delete [] p;
[231]216        }
217        else if(strcmp(buffer, "request_optimization") == 0) {
[230]218           
219            /* Receive Set of Observables */
[231]220            LOG("\nCognitive Engine:: Receiving Observable Parameters\n");
[230]221
[222]222            memset(buffer, 0, 256);
223            ReadMessage(commandSocketFD,buffer);
224            uint32_t numObservables = atoi(buffer);
225   
[224]226            Observable *o = new Observable[numObservables];
[222]227 
[224]228            for(size_t i = 0; i < numObservables; i++) {
[222]229                memset(buffer, 0, 256);
[224]230                ReadMessage(commandSocketFD, buffer);
[222]231                o[i].name = std::string(buffer);
232   
233                memset(buffer, 0, 256);
[224]234                ReadMessage(commandSocketFD, buffer);
[222]235                o[i].value = atof(buffer);
236            } 
237
[228]238            /* Receive Set of current Parameters */
[231]239            LOG("Cognitive Engine:: Receiving Current Transmission Parameters\n");
[228]240
241            memset(buffer, 0, 256);
[231]242            ReadMessage(commandSocketFD, buffer);
[228]243            uint32_t numCurrentParameters = atoi(buffer);
244   
245            Parameter *cp = new Parameter[numCurrentParameters];
246 
247            for(size_t i = 0; i < numCurrentParameters; i++) {
248                memset(buffer, 0, 256);
249                ReadMessage(commandSocketFD, buffer);
250                cp[i].name = std::string(buffer);
251   
252                memset(buffer, 0, 256);
253                ReadMessage(commandSocketFD, buffer);
254                cp[i].value = atof(buffer);
255            } 
[231]256            LOG("Cognitive Engine:: Processing parameters....\n");
[224]257
[228]258            Parameter *solutionSet;
[231]259           
260            solutionSet = GetSolution(o,cp);
[228]261
[224]262            // TODO need to actually do something with the observables here
[230]263           
[231]264            LOG("Cognitive Engine:: Sending Optimal Parameters to Application.\n");
265            char numParametersChar[10];
266            char solutionValue[50];
267            sprintf(numParametersChar, "%i", radioInfo->numParameters);
268            SendMessage(commandSocketFD, numParametersChar);
[228]269            for(size_t i = 0; i < radioInfo->numParameters; i++) {
[231]270                SendMessage(commandSocketFD, solutionSet[i].name.c_str());
[228]271                memset(solutionValue, 0, 50);
[231]272                sprintf(solutionValue, "%f", solutionSet[i].value);
273                SendMessage(commandSocketFD, solutionValue);
274            }
[224]275
276            delete [] o;
[228]277            delete [] cp;
[218]278        }
279        else if(strcmp(buffer, "query_component_type") == 0) {
280            SendComponentType();
281        }
282        else if(strcmp(buffer, "connect_sml") == 0) {
283            /* This command implies that we are disconnecting from the shell and
284             * connecting to a SML component. */
285            char serverName[256];
286            char serverPort[256];
287            // TODO is this going to end up being too slow?
288            memset(serverName, 0, 256);
289            memset(serverPort, 0, 256);
290
291            ReadMessage(commandSocketFD, serverName);
292            ReadMessage(commandSocketFD, serverPort);
293
294            /* Only continue if we are currently connected to a shell. */
295            if(!SML_present) {
296                DeregisterComponent();
297
298                shutdown(commandSocketFD, 2);
299                close(commandSocketFD);
300
301                ConnectToRemoteComponent(serverName, serverPort, true);
302            }
303        }
304        else if(strcmp(buffer, "disconnect_sml") == 0) {
305            /* This command implies that we are disconnecting from the SML and
306             * connecting to a shell component. */
307            char serverName[256];
308            char serverPort[256];
309            // TODO is this going to end up being too slow?
310            memset(serverName, 0, 256);
311            memset(serverPort, 0, 256);
312
313            ReadMessage(commandSocketFD, serverName);
314            ReadMessage(commandSocketFD, serverPort);
315
316            /* We only want to do this if we are actually connected to an SML
317             * currently. */
318            if(SML_present) {
319                DeregisterServices();
320
321                shutdown(commandSocketFD, 2);
322                close(commandSocketFD);
323
324                ConnectToRemoteComponent(serverName, serverPort, false);
325            }
326        }
327        else if(strcmp(buffer, "reset_engine_cognitive") == 0) {
328            Reset();
329        }
330        else if(strcmp(buffer, "shutdown_engine_cognitive") == 0) {
331            Shutdown();
332        }
333    }
334}
335
336
337void
338CognitiveEngine::Shutdown()
339{
340    if(SML_present) {
341        DeregisterServices();
342        DeregisterComponent();
343    }
344    else {
345        DeregisterComponent();
346    }
347    // TODO should something else be happening here?
348}
349
350
351void
352CognitiveEngine::Reset()
353{
354    LOG("Resetting Cognitive Engine.\n");
355
356    if(SML_present) {
357        DeregisterServices();
358        DeregisterComponent();
359    }
360    else {
361        DeregisterComponent();
362    }
363}
364
365
366void
367CognitiveEngine::RegisterComponent()
368{
369    SendMessage(commandSocketFD, "register_engine_cognitive");
370    LOG("Cognitive Engine:: Registration message sent to shell.\n");
371}
372
373void
374CognitiveEngine::DeregisterComponent()
375{
376    SendMessage(commandSocketFD, "deregister_engine_cognitive");
377    LOG("Cognitive Engine:: Deregistration message sent.\n");
378
379    shutdown(commandSocketFD, 2);
380    close(commandSocketFD);
381    commandSocketFD = -1;
382    LOG("Cognitive Engine:: Shell socket closed.\n");
383}
384
385
386void
387CognitiveEngine::RegisterServices()
388{
389    LOG("Cognitive Engine:: Registering services.\n");
390
391    SendMessage(commandSocketFD, "register_service");
392    SendMessage(commandSocketFD, "jam_wifi");
393
394    SendMessage(commandSocketFD, "register_service");
395    SendMessage(commandSocketFD, "signal_detection");
396
397    SendMessage(commandSocketFD, "register_service");
398    SendMessage(commandSocketFD, "max_throughput");
399
400    SendMessage(commandSocketFD, "register_service");
401    SendMessage(commandSocketFD, "jam_bluetooth");
402}
403
404
405void
406CognitiveEngine::DeregisterServices()
407{
408    LOG("Cognitive Engine:: Deregistering services.\n");
409
410    SendMessage(commandSocketFD, "deregister_service");
411    SendMessage(commandSocketFD, "jam_wifi");
412
413    SendMessage(commandSocketFD, "deregister_service");
414    SendMessage(commandSocketFD, "signal_detection");
415
416    SendMessage(commandSocketFD, "deregister_service");
417    SendMessage(commandSocketFD, "max_throughput");
418
419    SendMessage(commandSocketFD, "deregister_service");
420    SendMessage(commandSocketFD, "jam_bluetooth");
421
422}
423
424void
425CognitiveEngine::ReceiveRadioConfiguration()
426{
427    LOG("Cognitive Engine:: Receiving Radio Configuration.\n");
428   
429    char buffer[256];
430 
431    /* Receive Set of Utilities */
432    memset(buffer, 0, 256);
[231]433    ReadMessage(commandSocketFD, buffer);
[218]434    radioInfo->numUtilities = atoi(buffer);
435   
[224]436    for(size_t i = 0; i < radioInfo->numUtilities; i++) {
[218]437        memset(buffer, 0, 256);
[231]438        ReadMessage(commandSocketFD, buffer);
[218]439        uList[i].name = std::string(buffer);
[228]440   
[218]441        memset(buffer, 0, 256);
[231]442        ReadMessage(commandSocketFD, buffer);
[218]443        uList[i].units = std::string(buffer);
444
445        memset(buffer, 0, 256);
[231]446        ReadMessage(commandSocketFD, buffer);
[218]447        uList[i].goal = std::string(buffer);
448   
449        memset(buffer, 0, 256);
[231]450        ReadMessage(commandSocketFD, buffer);
[218]451        uList[i].target = atof(buffer);
452    }
453
454    /* Receive Set of Parameters */
455    memset(buffer, 0, 256);
456    ReadMessage(commandSocketFD, buffer);
457    radioInfo->numParameters = atoi(buffer);
458   
459    for(size_t i = 0; i < radioInfo->numParameters; i++) {
460        memset(buffer, 0, 256);
461        ReadMessage(commandSocketFD, buffer);
462        pList[i].name = std::string(buffer);
463   
464        memset(buffer, 0, 256);
465        ReadMessage(commandSocketFD, buffer);
466        pList[i].units = std::string(buffer);
467
468        memset(buffer, 0, 256);
469        ReadMessage(commandSocketFD, buffer);
470        pList[i].min = atof(buffer);
471   
472        memset(buffer, 0, 256);
473        ReadMessage(commandSocketFD, buffer);
474        pList[i].max = atof(buffer);
475   
476        memset(buffer, 0, 256);
477        ReadMessage(commandSocketFD, buffer);
478        pList[i].step = atof(buffer);
479   
480        memset(buffer, 0, 256);
481        ReadMessage(commandSocketFD,buffer);
482        pList[i].numAffects = atoi(buffer);
483   
[224]484        for(size_t j = 0; j < pList[i].numAffects; j++) {
[218]485            memset(buffer, 0, 256);
486            ReadMessage(commandSocketFD,buffer);
[231]487            // TODO for + if{break} = while?
[224]488            for(size_t k = 0; k < radioInfo->numUtilities; k++) {
[231]489                if(uList[k].name == std::string(buffer)) {   
[218]490                    pList[i].affection_list[j].u = &uList[k];   
491                    break;
492                }
493            }
494
495            memset(buffer, 0, 256);
[231]496            ReadMessage(commandSocketFD, buffer);
[218]497            pList[i].affection_list[j].relation = std::string(buffer);   
498        }
499    }   
500
501    /* Receive Set of Observables */
502    memset(buffer, 0, 256);
[231]503    ReadMessage(commandSocketFD, buffer);
[218]504    radioInfo->numObservables = atoi(buffer);
505   
[224]506    for(size_t i = 0; i < radioInfo->numObservables; i++) {
[218]507        memset(buffer, 0, 256);
[231]508        ReadMessage(commandSocketFD, buffer);
[218]509        oList[i].name = std::string(buffer);
510   
511        memset(buffer, 0, 256);
[231]512        ReadMessage(commandSocketFD, buffer);
[218]513        oList[i].numAffects = atoi(buffer);
514   
[224]515        for(size_t j = 0; j < oList[i].numAffects; j++) {
[218]516            memset(buffer, 0, 256);
[231]517            ReadMessage(commandSocketFD, buffer);
518            // TODO for + if{break} = while?
[224]519            for(size_t k = 0; k < radioInfo->numUtilities; k++) {
[231]520                if(uList[k].name == std::string(buffer)){   
[218]521                    oList[i].affection_list[j].u = &uList[k];   
522                    break;
523                }
524            }
525 
526            memset(buffer, 0, 256);
[231]527            ReadMessage(commandSocketFD, buffer);
[218]528            oList[i].affection_list[j].relation = std::string(buffer);   
529        }
530    }
531
532    SendMessage(commandSocketFD, "receive_config_ack");
[228]533
[231]534    BuildCognitiveEngine();
[218]535}
536
537void
538CognitiveEngine::ReceiveExperience()
539{
540    LOG("Cognitive Engine:: Receiving Experience Report.\n");
541    char buffer[256];
542    uint32_t numberExp;
543   
544    /* Receive number of experience entries */
545    memset(buffer, 0, 256);
[231]546    ReadMessage(commandSocketFD, buffer);
[218]547    numberExp = atoi(buffer);
548
[231]549    LOG("Cognitive Engine:: Waiting for %i number of entries.\n", numberExp);
[218]550 
551    SendMessage(commandSocketFD, "receive_exp_ack");
552}
553
[228]554Parameter*
555CognitiveEngine::GetSolution(Observable *observables, Parameter *currentParameters)
[218]556{
557    LOG("Cognitive Engine:: Generating solution.\n");
[228]558
559    char *searchNames[radioInfo->numUtilities];
560
561    for(size_t i = 0; i < radioInfo->numUtilities; i++) {
[230]562        searchNames[i] = (char*)observables[i].name.c_str();
[231]563    }
[228]564
565    float searchVals[radioInfo->numUtilities];
566
[231]567    for(size_t i = 0; i < radioInfo->numUtilities; i++) {
568        searchVals[i] = uList[i].target;
[228]569    }
570
571    uint32_t numberColumns =
[231]572        radioInfo->numUtilities +
573        radioInfo->numParameters +
574        radioInfo->numObservables + 1;
575   
576    float returnValues[numberColumns];
577   
578    int searchOps[radioInfo->numUtilities];
[228]579    for(size_t i = 0; i < radioInfo->numUtilities; i++) {
580
581        /* If the goal is to maximum, set the search operation to
[231]582         * return values greater than the target.
583         *
584         * If the goal is to minimize, set the search operation to
585         * return values less than the target.
586         */
587        if(strcmp(uList[i].goal.c_str(), "max") == 0) {
588            searchOps[i] = GT;
589        } else if(strcmp(uList[i].goal.c_str(), "min") == 0) {
590            searchOps[i] = LT;
591        }
592    }
[228]593
[231]594    /* CBR specific call */
595    uint32_t rc = cbr_search(myCBR, searchNames, searchOps, searchVals,
596            radioInfo->numUtilities, returnValues);
[228]597
[231]598    if(rc == 0){
[228]599        /* Adapt the returned parameters to meet the objective */
[230]600        WARNING("Cognitive Engine:: Found\n");
[228]601
[231]602        /* Should do a random adaptation.. */
603        if(returnValues[numberColumns-1] < 0) {
604            returnValues[2] = returnValues[2] - 15;
605            returnValues[3] = returnValues[3] - 2;
[230]606        } else {
[231]607            returnValues[2] = returnValues[2] + 15;
608            returnValues[3] = returnValues[3] + 2;
609        }
610    } else if(rc == 31337) {
[230]611        WARNING("Cognitive Engine:: Not Found.\n");
[231]612        /* No rows in the CBR, pick default parameters */
613        /* Currently this is hard coded and implementation specific! */
614        returnValues[2] = currentParameters[0].value + 5;
615        returnValues[3] = currentParameters[1].value + 10;
[228]616       
[231]617    } else {
[228]618        WARNING("Cognitive Engine:: Search return an invalid value.\n");
[231]619    }
[228]620
[231]621    size_t returnValueIndex = 0;
[228]622    for(size_t i = 0; i < radioInfo->numUtilities; i++) {
[231]623        uList[i].value = returnValues[returnValueIndex];
624        returnValueIndex++;
625    }
[228]626    for(size_t i = 0; i < radioInfo->numParameters; i++) {
[231]627        pList[i].value = returnValues[returnValueIndex];
628        returnValueIndex++;
629    }
[228]630    for(size_t i = 0; i < radioInfo->numObservables; i++) {
[231]631        oList[i].value = returnValues[returnValueIndex];
632        returnValueIndex++;
633    }
[230]634    returnValues[returnValueIndex] = 0;
[228]635
[230]636    char *allNames[numberColumns];
[231]637    size_t allNameIndex = 0;
[230]638    for(size_t i = 0; i < radioInfo->numUtilities; i++) {
639        allNames[allNameIndex] = (char*)uList[i].name.c_str();
[231]640        returnValues[allNameIndex] = uList[i].target;
641        allNameIndex++;
642    }
[230]643    for(size_t i = 0; i < radioInfo->numParameters; i++) {
644        allNames[allNameIndex] = (char*)pList[i].name.c_str();
[231]645        allNameIndex++;
646    }
[230]647    for(size_t i = 0; i < radioInfo->numObservables; i++) {
648        allNames[allNameIndex] = (char*)oList[i].name.c_str();
[231]649        returnValues[allNameIndex] = 0;
650        allNameIndex++;
651    }
[230]652    allNames[allNameIndex] = "utility";
653
[231]654    // Add row to CBR.
655    cbr_add_row(myCBR, allNames, returnValues, returnValueIndex+1);
[228]656
[231]657    return pList;
[218]658}
659
[228]660
661Parameter*
[231]662CognitiveEngine::GetSolution(Observable *observables, \
663        Parameter *currentParameters, std::string service)
[218]664{
[231]665    LOG("Cognitive Engine:: Generating solution for %s service.\n", service.c_str());
[228]666
667    return pList;
[218]668}
669
[228]670
[218]671void
672CognitiveEngine::ReceiveFeedback(Observable *observables, Parameter *parameters, \
[230]673    std::string service)
[218]674{
675    LOG("Cognitive Engine:: Receiving feedback.\n");
676}
677
[228]678
679void
680CognitiveEngine::BuildCognitiveEngine()
681{
[231]682    char filename[] = {"ex1"};
683    char tablename[] = {"data"};
[228]684
685    uint32_t numberColumns =
[231]686        radioInfo->numUtilities +
687        radioInfo->numParameters +
688        radioInfo->numObservables + 1;
[228]689
690    char *cols[numberColumns];
691
692    size_t columnIndex = 0;
693    for (size_t i = 0; i < radioInfo->numUtilities; i++){
[231]694        cols[columnIndex] = (char*)uList[i].name.c_str();
[228]695        columnIndex++;
696    }   
697    for (size_t i = 0; i < radioInfo->numParameters; i++){
[231]698        cols[columnIndex] = (char*)pList[i].name.c_str();
[228]699        columnIndex++;
700    }   
701    for (size_t i = 0; i < radioInfo->numObservables; i++){
[231]702        cols[columnIndex] = (char*)oList[i].name.c_str();
[228]703        columnIndex++;
704    }   
705    cols[columnIndex] = "utility";
706
707    myCBR = cbr_create(filename, tablename, cols, numberColumns);
708}
709
Note: See TracBrowser for help on using the browser.