root/applications/doprava/aimsun_bdm/aimsun_ds.cpp @ 874

Revision 874, 18.0 kB (checked in by prikryl, 14 years ago)

Constructor of AimsunDS needs user-info config as parameter.
AimsunDS accepts two parameters from config file: entrances and stop_time.

Line 
1/*
2 */
3
4/* Class definition */
5#include "aimsun_ds.h"
6#include "tools.h"
7
8// TODO: reference additional headers your program requires here
9#include <process.h>
10#include <strsafe.h>
11#include <shlwapi.h>
12#include <io.h>
13
14//#include <atlbase.h>
15
16#define MAX_PATH_BYTES  (MAX_PATH*sizeof(TCHAR))
17#define MAX_EXE_BYTES   (MAX_EXE_PATH*sizeof(TCHAR))
18
19/* Contoller list is hardwired for Aimsun scenario `zlicin_495_601.sce` */
20#define NUM_CONTROLLERS 2
21const TCHAR * ControllerList[NUM_CONTROLLERS] = { TEXT("K_5_495"), TEXT("K_5_601") };
22const TCHAR * WindowStr[NUM_CONTROLLERS]      = { TEXT("els3@5.495"), TEXT("els3@5.601") };
23const int IntersectionIDs[] = { 495, 601 }; 
24const int NumIntersections = sizeof ( IntersectionIDs );
25
26/* Entrance sections are hardwired for Aimsun scenario `zlicin_495_601.sce` */
27const int EntranceSections[] = { 1, 42, 45, 287, 26 };
28const int NumEntranceSections = sizeof(EntranceSections)/sizeof(int);
29
30/* Identifiers of sections that Aimsun shall collect statistics for are also
31   hardwired for the scenario `zlicin_495_601.sce`. */
32const int StatIds[] = {
33        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
34        26, 27, 35, 28, 29, 30, 31, 32, 33, 34, 39, 36, 37, 38, 21, 22, 23, 24, 25, 40,
35        42, 43, 44, 45, 49, 46, 47, 50, 51, 287, 288, 54, 55, 56, 57, 58, 59, 60, 61,
36        62, 63, 64, 65, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77 };
37const int NumStatIds = sizeof(StatIds)/sizeof(int);
38
39
40const TCHAR * PatternsELS3[] = { TEXT("GetramELS3.dll"), TEXT("GetramELS3d.dll") };
41const TCHAR * PatternsVGS[] = { TEXT("GetramVGS.dll"), TEXT("GetramVGSd.dll") };
42
43/* Offsets of `dt` components (dt is the vector returned by AimsunDS, it contains composite
44   values of all measurements for all intersections in the system). */
45const int SignalPlanOffsets[] = {  0, 36 };
46const int MeasurementOffsets[]  = {  6, 42 };
47
48AimsunDS::AimsunDS ( UIFile &cfg ) : DS()
49{
50        HKEY  hKey;
51        TCHAR szBasePath[MAX_PATH];             /**< Base path of the whole application, root of the tree. */
52        TCHAR szDllPath[MAX_PATH];              /**< Directory where all DLLs reside. Will be added to PATH. */
53        TCHAR szSceDllPath[MAX_PATH];   /**< Temporary storage for DLL path rewritten in the scenario file. */
54        TCHAR szSceDir[MAX_PATH];               /**< Directory of the simulated scenario. */
55        TCHAR szScePath[MAX_PATH];              /**< Full path to the Aimsun scenario file. The file resides in `szSceDir`. */
56        TCHAR szScePathA[MAX_PATH];
57        TCHAR szScePathB[MAX_PATH];
58        TCHAR szScePathC[MAX_PATH];
59        TCHAR szNetPath[MAX_PATH];
60        TCHAR szHomeDir[MAX_PATH];              /**< Installation directory of Aimsun 4.2.x */
61        TCHAR szELS3Path[MAX_PATH];             /**< Full path of the simulator of ELS3 controller. */
62        TCHAR szExePath[MAX_EXE_PATH];  /**< Command line when staring ELS3 controllers. */
63        TCHAR szEntrancePath[MAX_PATH]; /**< Points to the CSV file with vehicle entrances for the simulation. */
64        string entranceFileName;
65        string stopTime;
66        DWORD dwBufLen = MAX_PATH_BYTES;
67        LONG  res;
68
69        intptr_t hPid;
70
71        int verIndex = 1; //@TODO@: 0 is for release, 1 for debug build of the DLL libraries.
72
73        /* The root directory of the simulator tree is defined externally
74           in "CMakeLists.txt". */
75        StringCbCopy ( szBasePath, MAX_PATH_BYTES, BASE_PATH );
76
77        /* Create the path to DLL directory. The path is defined externally
78           in  "CMakeLists.txt".
79           The path will be used to inject the appropriate location of
80           Getram extensions into the selected Aimsun scenario and also
81           to modify PATH variable of executed processes in order to
82           provide access to API DLLs. */
83        StringCbCopy ( szDllPath, MAX_PATH_BYTES, DLL_PATH );
84
85        /* Add the DLL directory to the PATH used by this process.
86           The PATH will be passed to all other processes started
87           by this one and hence they will be able to find API
88           DLLs. */
89        _addpath ( szDllPath );
90
91#ifdef LATER_OR_NEVER
92        GetPrivateProfileString (
93                TEXT("paths"),
94                TEXT("base"),
95                NULL,
96                szBasePath,
97                MAX_PATH,
98                TEXT(".\\simulate.ini")
99                );
100#endif
101
102        /* Find the location of Aimsun executable. It is stored in registry tree as
103           \\HKEY_LOCAL_MACHINE\SOFTWARE\TSS-Transport Simulation Systems\GETRAM\4.2.0 */
104        res = RegOpenKeyEx (
105                HKEY_LOCAL_MACHINE,
106                TEXT("SOFTWARE\\TSS-Transport Simulation Systems\\GETRAM\\4.2.0"),
107                0,
108                KEY_QUERY_VALUE,
109                &hKey
110                );
111        if ( res != ERROR_SUCCESS )
112        {
113                perror ( "Cannot get handle to Aimsun registry entry" );
114                return;
115        }
116
117        /* The executable location is `HomeDir` key. */
118        res = RegQueryValueEx (
119                hKey,
120                TEXT("HomeDir"),
121        NULL, NULL,
122                (LPBYTE) szHomeDir,
123                &dwBufLen
124                );
125        RegCloseKey( hKey );
126    if (( res != ERROR_SUCCESS ) || ( dwBufLen > MAX_PATH_BYTES ))
127        {
128                perror ( "Cannot read the Aimsun home directory from registry" );
129        return;
130        }
131
132        /* Concatenate the home path with the executable name. */
133        StringCbCat ( szHomeDir, MAX_PATH_BYTES, TEXT("\\aimsun42.exe") );
134
135        /* Create the version of home path including the quotation marks. */
136        StringCbCopy ( szExePath, MAX_EXE_PATH, TEXT("\"") );
137        StringCbCat ( szExePath, MAX_EXE_PATH, szHomeDir );
138        StringCbCat ( szExePath, MAX_EXE_PATH, TEXT("\"") );
139
140        /* Create the path to ELS3 executable. */
141        StringCbCopy ( szELS3Path, MAX_PATH_BYTES, TEXT("\"") );
142        StringCbCat ( szELS3Path, MAX_PATH_BYTES, szBasePath );
143        StringCbCat ( szELS3Path, MAX_PATH_BYTES, TEXT("\\els3\\els3sample.exe\"") );
144
145        /* Aimsun scenario modification.
146           We have to take care of items that are specified by absolute
147           paths in the scenario file. These items include:
148           - network path (section #NETWORK),
149           - ELS3 Getram extension DLL (section #EXTENSIONS),
150           - VGS Getram extension DLL (section #EXTENSIONS).
151
152           We will start with constructing the new network path.*/
153        StringCbCopy ( szSceDir, MAX_EXE_PATH, szBasePath );
154        StringCbCat ( szSceDir, MAX_EXE_PATH, TEXT("\\scenarios\\zlicin_495_601") );
155        StringCbCopy ( szNetPath, MAX_EXE_PATH, szSceDir );
156        StringCbCat ( szNetPath, MAX_EXE_PATH, TEXT("\\zlicin_495_601") );
157
158        /* Our convention is that the scenario file name corresponds
159           to the network path name. */
160        StringCbCopy ( szScePath, MAX_EXE_PATH, szNetPath );
161        StringCbCat ( szScePath, MAX_EXE_PATH, TEXT(".sce") );
162
163        /* First modification step: Replace the location of ELS3 Getram
164           extension.
165           Start with constructing full path to the extension DLL.*/
166        StringCbCopy ( szSceDllPath, MAX_EXE_PATH, szDllPath );
167        StringCbCat ( szSceDllPath, MAX_EXE_PATH, TEXT("\\") );
168        StringCbCat ( szSceDllPath, MAX_EXE_PATH, PatternsELS3[verIndex] );
169        /* We are not allowed to modify scenario in place (it is part
170           of SVN snapshot and we do not want our local changes to
171           propagate to the trunk with every commit). Therefore we
172           need an alternative scenario name that will be constructed
173           now. */
174        StringCbCopy ( szScePathA, MAX_EXE_PATH, szScePath );
175        StringCbCat ( szScePathA, MAX_EXE_PATH, TEXT(".a") );
176        /* And do the replacements. */
177        replace_in_scenario (
178                szScePath, szScePathA,
179                TEXT("#EXTENSIONS"),
180                PatternsELS3,
181                2,
182                szSceDllPath
183                );
184
185        /* Second modification step.
186           Replace the location of VGS Getram extension. */
187        StringCbCopy ( szSceDllPath, MAX_EXE_PATH, szDllPath );
188        StringCbCat ( szSceDllPath, MAX_EXE_PATH, TEXT("\\") );
189        StringCbCat ( szSceDllPath, MAX_EXE_PATH, PatternsVGS[verIndex] );
190        StringCbCopy ( szScePathB, MAX_EXE_PATH, szScePath );
191        StringCbCat ( szScePathB, MAX_EXE_PATH, TEXT(".b") );
192        replace_in_scenario (
193                szScePathA, szScePathB,
194                TEXT("#EXTENSIONS"),
195                PatternsVGS,
196                2,
197                szSceDllPath
198                );
199
200        const TCHAR * pattern_net[] = { TEXT("zlicin_495_601") };
201
202        /* Third modification step.
203           Replace the network path. */
204        StringCbCopy ( szScePathC, MAX_EXE_PATH, szScePath );
205        StringCbCat ( szScePathC, MAX_EXE_PATH, TEXT(".c") );
206        replace_in_scenario (
207                szScePathB, szScePathC,
208                TEXT("#NETWORK"),
209                pattern_net,
210                1,
211                szNetPath
212                );
213
214        /* Fourth modification step.
215           Replace the `stop time` field of RunTime information. */
216        UI::get ( stopTime, cfg, "stop_time" );
217        replace_stoptime (
218                szNetPath,
219                stopTime.c_str()
220                );
221
222        StringCbCat ( szScePath, MAX_EXE_PATH, TEXT("\"") );
223        StringCbCat ( szScePath, MAX_EXE_PATH, TEXT("\"") );
224
225        /* Spawn the process. */
226        hPid = _tspawnl (
227                _P_NOWAIT,
228                szHomeDir,
229                szExePath,
230                TEXT("-run"),
231                szScePathC,
232                NULL
233                );
234
235        if ( hPid < 0 )
236        {
237                perror ( "Cannot start Aimsun by _spawnl()" );
238                return;
239        }
240
241        /* Now that Aimsun is running, we need to start also the controllers. */
242        for ( int c = 0 ; c < NUM_CONTROLLERS ; c++ )
243        {
244                TCHAR szELS3Config[MAX_PATH];
245                szELS3Config[0] = 0;
246                StringCbCat ( szELS3Config, MAX_PATH_BYTES, TEXT("\"") );
247                StringCbCat ( szELS3Config, MAX_PATH_BYTES, szBasePath );
248                StringCbCat ( szELS3Config, MAX_PATH_BYTES, TEXT("\\els3\\configs\\") );
249                StringCbCat ( szELS3Config, MAX_PATH_BYTES, ControllerList[c] );
250                StringCbCat ( szELS3Config, MAX_PATH_BYTES, TEXT(".ini\"") );
251
252                /* Create the command line for the controller. */
253                szExePath[0] = 0;
254                StringCbCat ( szExePath, MAX_EXE_PATH, TEXT("start \"") );
255                StringCbCat ( szExePath, MAX_EXE_PATH, WindowStr[c] );
256                StringCbCat ( szExePath, MAX_EXE_PATH, TEXT("\" ") );
257                StringCbCat ( szExePath, MAX_EXE_PATH, szELS3Path );
258                StringCbCat ( szExePath, MAX_EXE_PATH, TEXT(" -l frm,det,spl") );
259                StringCbCat ( szExePath, MAX_EXE_PATH, TEXT(" -c 12.12.2007 00:00:00") );
260                StringCbCat ( szExePath, MAX_EXE_PATH, TEXT(" -f ") );
261                StringCbCat ( szExePath, MAX_EXE_PATH, szELS3Config );
262
263                /* Spawn the process. */
264                _tsystem ( szExePath );
265        }
266
267        /* Aaaaggggrrrrrhhhh!
268           C++ is much much faster than Matlab and we have a synchronisation
269           issue with els3 controllers starting too late. */
270        Sleep ( 5000 );
271
272        /* Query the configuration file for the name of the file containsing
273           vehicle entrances. */
274        UI::get ( entranceFileName, cfg, "entrances" );
275
276        /* Prepare full path for vehicle input data. */
277        StringCbCopy ( szEntrancePath, MAX_EXE_PATH, szSceDir );
278        StringCbCat  ( szEntrancePath, MAX_EXE_PATH, TEXT("\\") );
279        StringCbCat  ( szEntrancePath, MAX_EXE_PATH, entranceFileName.c_str() );
280
281        /* Initialise and start the vehicle input.
282           The function call refers to delay-loaded DLL, so expect possible
283           complainst in case that the DLL canot be found in PATH. */
284        initVGS (
285                HEADWAY_UNIFORM,
286                szEntrancePath,
287                TEXT("sect_stats.csv"),
288                TEXT("glob_stats.csv")
289                );
290
291  /* Description of the channels of the data vector. */
292  Drv = RV ( "{"
293    "495_VA  495_VB   495_VC   495_VD   495_VE   495_VF "
294    "495_DVA 495_DVB  495_DVA1 495_DVB1 495_DVC  495_DVD  495_DVE  495_DVF  495_DVF1 "
295    "495_S1  495_S2   495_S3   495_S4   495_S5   495_S5a "
296    "601_VA  601_VB   601_VC   601_VD   601_VE   601_SE "
297        "601_DVA 601_DVAa 601_DVB  601_DVBa 601_DVB1 601_DVC  601_DVD  601_DVD1 601_DSE 601_DVE 601_DSE1 601_DVE1 "
298    "601_S6  601_S6a  601_S7   601_S8   601_S9   601_S9a "
299    "}",
300        "    1,      1,      1,      1,      1,       1,      "
301        "    2,      2,      2,      2,      2,       2,      2,      2,      2,"
302        "    2,      2,      2,      2,      2,       2,"
303        "    1,      1,      1,      1,      1,       1,      "
304        "    2,      2,      2,      2,      2,       2,       2,       2,       2,       2,       2,       2,"
305        "    2,      2,      2,      2,      2,       2"
306        );
307  /* Remember the size of the data vector. */
308  dtsize = Drv._dsize();
309
310  /* Description of the channels of the input vector. */
311  Urv = RV ( "{"
312          "Tc"
313          "495_offset"
314          "495_VA  495_VB   495_VC   495_VD   495_VE   495_VF "
315          "601_offset"
316          "601_VA  601_VB   601_VC   601_VD   601_VE   601_SE "
317          "}" );
318  /* Remember the size of the input vector. */
319  utsize = Urv._dsize();
320
321  /* Initialise the pointer to received data. */
322  p_rsp = NULL;
323}
324
325void AimsunDS::initVGS (
326        const vgs_headway_mode headway,
327        const string &entrancePath,
328        const string &sectionStatsPath,
329        const string &globalStatsPath
330        )
331{
332    int res;
333       
334        /* Pass information about entrance sections to VGS API (and so to the
335       GetramVGS extension that will be generating vehicles for these
336           entrances). */
337    vgs_set_entrance_sections ( EntranceSections, NumEntranceSections );
338   
339    /* Check the validity of the headway mode. */
340    res = vgs_is_valid_headway_mode ( headway );
341    if ( !res )
342        {
343        fprintf ( stderr, "Invalid headway mode '%d'\n", headway );
344                exit ( -1 );
345        }
346   
347    /* Specify where to find the CSV with vehicle entrances and which
348           headway mode to use for generating vehicle flows. */
349        vgs_generate_vehicles ( entrancePath.c_str(), headway );
350   
351    /* Pass to VGS API information about section identifiers we want to
352       collect statistics for (and VGS will pass it to the GetramVGS
353       extension). */ 
354        vgs_set_stats_sections ( StatIds, NumStatIds );
355   
356    /* Specify where to store CSV files with statistical data. */
357        vgs_set_stats_file_names ( sectionStatsPath.c_str(), globalStatsPath.c_str() );
358   
359    /* Specify the maximum queuing speed in kmph. */
360    vgs_set_max_qspeed ( 3.6f );
361   
362    /* Unlock the semaphore blocking Aimsun from execution. */
363    vgs_unlock_aimsun();
364}
365
366void AimsunDS::getdata ( vec &dt ) const
367{
368        /* Return the last data. */
369        printf ( "\n\tAimsunDS::getdata() starting\n" );
370
371        if ( p_rsp == NULL )
372        {
373                fprintf ( stderr, "ERROR: AimsunDS::getdata() - no data available yet!\n" );
374                return;
375        }
376
377        /* Loop over all controllers in the measurement set. */
378        for ( int i = 0; i < p_rsp->count; i++)
379        {
380                det_stats * ds_ptr  = p_rsp->dets + i;
381                sp_stats *  sp_ptr  = p_rsp->sps  + i;
382
383                printf ( "\tRealised signal plans:\n" );
384
385                /* Loop over all measurement sets. We will deliver just
386                   the last one. */
387                for ( int j = 0; j < sp_ptr->count ; j++ )
388                {
389                        sp_stattab * dat_ptr = sp_ptr->splans + j;
390                        int          pos     = _search_index ( IntersectionIDs, NumIntersections, dat_ptr->its_id );
391                        int          offset  = SignalPlanOffsets[pos];
392
393                        printf ( "\t[%3d] t=%6ds ", j, dat_ptr->global_time );
394
395                        /* Copy particular signals. */
396                        for ( int k = 0 ; k < dat_ptr->num_siggroups ; k++ )
397                        {
398                                dt[k+offset] = dat_ptr->green_time[k];
399                                printf ( "%3d ", dat_ptr->green_time[k] );
400                        }
401                        printf ( "\n" );
402                }
403
404                printf ( "\n\tDetector data:\n" );
405
406                /* Loop over all mesurement records with intensities and
407                   occupancies. */
408                for ( int j = 0 ; j < ds_ptr->count ; j++ )
409                {
410                        det_aggreg_table * dat_ptr = ds_ptr->stats + j;
411                        int  pos     = _search_index ( IntersectionIDs, NumIntersections, dat_ptr->its_id );
412                        int  moffset = MeasurementOffsets[pos];
413
414                        printf ( "\t[%3d] t=%6ds ", j, dat_ptr->global_time );
415
416                        for ( int k = 0 ; k < dat_ptr->num_det ; k++ )
417                        {
418                                dt[2*k+moffset]   = dat_ptr->dt_intensity[k];
419                                dt[2*k+moffset+1] = dat_ptr->dt_occupancy[k];
420                                printf ( "{%2d/%2d} ",
421                                        dat_ptr->dt_intensity[k], dat_ptr->dt_occupancy[k] );
422                        }
423                        printf ( "\n" );
424                }
425        }
426
427        printf ( "\tAimsunDS::getdata() finished\n" );
428        cout << dt ;
429}
430
431
432void AimsunDS::write ( vec &ut )
433{
434    eh_res     sp_res;   /* Result code from write operation. */
435        eh_hrdels3 hrdsp[2]; /* Signal plan for two intersections. */
436   
437    /* Check that the length of the vector is correct. */
438    if ( ut.size() != utsize )
439    {
440      fprintf ( stderr, "Incorrect length of data vector (should be %d)", utsize );
441      exit(-1);
442    }
443    /* Check that the phase lengths sum to correct cycle time. */
444    /*
445        if ( ut[0]+ut[1]+ut[2] != 80 )
446    {
447      fprintf ( stderr, "Intersection 495: F1+F2+F3 != Tc (should be 80)" );
448      exit(-1);
449    }
450    if ( ut[3]+ut[4]+ut[5] != 80 )
451    {
452      fprintf ( stderr, "Intersection 601: F1+F2+F3 != Tc (should be 80)" );
453      exit(-1);
454    }*/
455
456    hrdsp[0].id = 495;
457    hrdsp[0].sp.cycle_time   = int ( ut[0] );
458        hrdsp[0].sp.offset       = int ( ut[1] );
459        hrdsp[0].sp.phase_len[0] = int ( ut[2] );
460    hrdsp[0].sp.phase_len[1] = int ( ut[3] );
461    hrdsp[0].sp.phase_len[2] = int ( ut[4] );
462    hrdsp[0].sp.phase_len[3] = -1;
463
464    hrdsp[1].id = 601;
465    hrdsp[1].sp.cycle_time   = int ( ut[ 0] );
466    hrdsp[1].sp.offset       = int ( ut[ 8] );
467    hrdsp[1].sp.phase_len[0] = int ( ut[ 9] );
468    hrdsp[1].sp.phase_len[1] = int ( ut[10] );
469    hrdsp[1].sp.phase_len[2] = int ( ut[11] );
470    hrdsp[1].sp.phase_len[3] = -1;
471
472        /* Write new signal plan to the shared memory of active controllers. */
473    sp_res = eh_write_hrd_data( hrdsp, 2 );
474
475        if ( sp_res != EH_RES_OK )
476        {
477                printf ( "eh_write_hrd_data(): " );
478                if ( sp_res == EH_RES_WCNT )
479                {
480                        printf ( "Wrong count of ELS3...\n" );
481                }
482                else if ( sp_res == EH_RES_WRID )
483                {
484                        printf ( "Wrong intersection ID...\n" );
485                }
486                else
487                {
488                        printf ( "Unknown error [%d] ...\n", sp_res );
489                }
490        }
491}
492
493
494void AimsunDS::step()
495{
496        eh_wres      wsp_res;   /* Result code of the wait operation. */
497    int          count;         /* Count of measurement sets. */
498   
499    /* Wait for 100 seconds until data from all ELS3 controllers arrive. */
500        wsp_res = eh_wait_on_rsp_complete( 100000 );
501
502        if ( wsp_res != EH_WRES_OK )
503        {
504                printf ( "eh_wait_on_rsp_complete(): " );
505
506                if ( wsp_res == EH_WRES_TIMEOUT )
507                {
508                        printf ( "Timeout elapsed ...\n" );
509                }
510                else if ( wsp_res == EH_WRES_EXIT )
511                {
512                        printf ( "Some ELS3 has exited ...\n" );
513                }
514                else
515                {
516                        printf ( "Unknown error [%d] ...\n", wsp_res );
517                }
518                exit(-1);
519        }
520
521        /* This will copy the detector data out. It is needed by the
522           Matlab HRDS version due to its internal structure - realised
523           signal plans are read asynchronously and later. The value
524       of `count` reports the number of data sets that will be
525       returned by the call to `eh_read_realised_sp()`. This coount
526       will be at least 1 but it may be higher in cases where the
527       controller reports measurements in short intervals but we are
528       reading them less frequently. */
529        count = eh_copy_out_measurements ();
530
531        printf ( "got %d measurements from controllers ...", count );
532
533        /* Read realised signal plan from shared memory. */
534        if ( eh_read_realised_sp( &p_rsp ) == EH_RES_OK )
535        {
536                printf("\nsuccesfully read data from controllers\n" );
537        }
538        else
539        {
540                printf(" but got no data ???\n");
541        }
542}
Note: See TracBrowser for help on using the browser.