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

Revision 833, 17.1 kB (checked in by prikryl, 14 years ago)

Cleaned the handling of szBasePath.

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