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

Revision 860, 17.6 kB (checked in by prikryl, 14 years ago)

New global varibles IntersectionIDs and NumIntersections.
Measured RV has two dimensions, which allows us to unify IntensityOffsets and OccupancyOffests into MeasurementOffsets.
Use DLL_PATH to tell us the location of the global DLL directory.
Do not call set_log_level().
Corrected the measured data processing loops in getdata().

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