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

Revision 825, 17.9 kB (checked in by prikryl, 14 years ago)

Initial commit of Aimsun data source and support tools.

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