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

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

Value of szBasePath is already the base path.
Different placement of windows includes.

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