/* */ /* Class definition */ #include "aimsun_ds.h" #include "tools.h" // TODO: reference additional headers your program requires here #include #include #include #include //#include #define MAX_PATH_BYTES (MAX_PATH*sizeof(TCHAR)) #define MAX_EXE_BYTES (MAX_EXE_PATH*sizeof(TCHAR)) /* Contoller list is hardwired for Aimsun scenario `zlicin_495_601.sce` */ #define NUM_CONTROLLERS 2 const TCHAR * ControllerList[NUM_CONTROLLERS] = { TEXT("K_5_495"), TEXT("K_5_601") }; const TCHAR * WindowStr[NUM_CONTROLLERS] = { TEXT("els3@5.495"), TEXT("els3@5.601") }; const int IntersectionIDs[] = { 495, 601 }; const int NumIntersections = sizeof ( IntersectionIDs ); /* Entrance sections are hardwired for Aimsun scenario `zlicin_495_601.sce` */ const int EntranceSections[] = { 1, 42, 45, 287, 26 }; const int NumEntranceSections = sizeof(EntranceSections)/sizeof(int); /* Identifiers of sections that Aimsun shall collect statistics for are also hardwired for the scenario `zlicin_495_601.sce`. */ const int StatIds[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 26, 27, 35, 28, 29, 30, 31, 32, 33, 34, 39, 36, 37, 38, 21, 22, 23, 24, 25, 40, 42, 43, 44, 45, 49, 46, 47, 50, 51, 287, 288, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77 }; const int NumStatIds = sizeof(StatIds)/sizeof(int); const TCHAR * PatternsELS3[] = { TEXT("GetramELS3.dll"), TEXT("GetramELS3d.dll") }; const TCHAR * PatternsVGS[] = { TEXT("GetramVGS.dll"), TEXT("GetramVGSd.dll") }; /* Offsets of `dt` components (dt is the vector returned by AimsunDS, it contains composite values of all measurements for all intersections in the system). */ const int SignalPlanOffsets[] = { 0, 36 }; const int MeasurementOffsets[] = { 6, 42 }; AimsunDS::AimsunDS () : DS() { /* Description of the channels of the data vector. */ Drv = RV ( "{" "495_VA 495_VB 495_VC 495_VD 495_VE 495_VF " "495_DVA 495_DVB 495_DVA1 495_DVB1 495_DVC 495_DVD 495_DVE 495_DVF 495_DVF1 " "495_S1 495_S2 495_S3 495_S4 495_S5 495_S5a " "601_VA 601_VB 601_VC 601_VD 601_VE 601_SE " "601_DVA 601_DVAa 601_DVB 601_DVBa 601_DVB1 601_DVC 601_DVD 601_DVD1 601_DSE 601_DVE 601_DSE1 601_DVE1 " "601_S6 601_S6a 601_S7 601_S8 601_S9 601_S9a " "}", " 1, 1, 1, 1, 1, 1, " " 2, 2, 2, 2, 2, 2, 2, 2, 2," " 2, 2, 2, 2, 2, 2," " 1, 1, 1, 1, 1, 1, " " 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2," " 2, 2, 2, 2, 2, 2" ); /* Remember the size of the data vector. */ dtsize = Drv._dsize(); /* Description of the channels of the input vector. */ Urv = RV ( "{" "Tc" "495_offset" "495_VA 495_VB 495_VC 495_VD 495_VE 495_VF " "601_offset" "601_VA 601_VB 601_VC 601_VD 601_VE 601_SE " "}" ); /* Remember the size of the input vector. */ utsize = Urv._dsize(); /* Initialise the pointer to received data. */ p_rsp = NULL; } void AimsunDS::from_setting ( const Setting &cfg ) { /* Check the `stop time` field in configuration. */ UI::get ( stopTime, cfg, "stop_time" ); /* Query the configuration file for the name of the file containsing vehicle entrances. */ UI::get ( entranceFileName, cfg, "entrances" ); } void AimsunDS::validate () { HKEY hKey; TCHAR szBasePath[MAX_PATH]; /**< Base path of the whole application, root of the tree. */ TCHAR szDllPath[MAX_PATH]; /**< Directory where all DLLs reside. Will be added to PATH. */ TCHAR szSceDllPath[MAX_PATH]; /**< Temporary storage for DLL path rewritten in the scenario file. */ TCHAR szSceDir[MAX_PATH]; /**< Directory of the simulated scenario. */ TCHAR szScePath[MAX_PATH]; /**< Full path to the Aimsun scenario file. The file resides in `szSceDir`. */ TCHAR szScePathA[MAX_PATH]; TCHAR szScePathB[MAX_PATH]; TCHAR szScePathC[MAX_PATH]; TCHAR szNetPath[MAX_PATH]; TCHAR szHomeDir[MAX_PATH]; /**< Installation directory of Aimsun 4.2.x */ TCHAR szELS3Path[MAX_PATH]; /**< Full path of the simulator of ELS3 controller. */ TCHAR szExePath[MAX_EXE_PATH]; /**< Command line when staring ELS3 controllers. */ TCHAR szEntrancePath[MAX_PATH]; /**< Points to the CSV file with vehicle entrances for the simulation. */ DWORD dwBufLen = MAX_PATH_BYTES; LONG res; intptr_t hPid; int verIndex = 1; //@TODO@: 0 is for release, 1 for debug build of the DLL libraries. /* The root directory of the simulator tree is defined externally in "CMakeLists.txt". */ StringCbCopy ( szBasePath, MAX_PATH_BYTES, BASE_PATH ); /* Create the path to DLL directory. The path is defined externally in "CMakeLists.txt". The path will be used to inject the appropriate location of Getram extensions into the selected Aimsun scenario and also to modify PATH variable of executed processes in order to provide access to API DLLs. */ StringCbCopy ( szDllPath, MAX_PATH_BYTES, DLL_PATH ); /* Add the DLL directory to the PATH used by this process. The PATH will be passed to all other processes started by this one and hence they will be able to find API DLLs. */ _addpath ( szDllPath ); #ifdef LATER_OR_NEVER GetPrivateProfileString ( TEXT("paths"), TEXT("base"), NULL, szBasePath, MAX_PATH, TEXT(".\\simulate.ini") ); #endif /* Find the location of Aimsun executable. It is stored in registry tree as \\HKEY_LOCAL_MACHINE\SOFTWARE\TSS-Transport Simulation Systems\GETRAM\4.2.0 */ res = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\TSS-Transport Simulation Systems\\GETRAM\\4.2.0"), 0, KEY_QUERY_VALUE, &hKey ); if ( res != ERROR_SUCCESS ) { perror ( "Cannot get handle to Aimsun registry entry" ); return; } /* The executable location is `HomeDir` key. */ res = RegQueryValueEx ( hKey, TEXT("HomeDir"), NULL, NULL, (LPBYTE) szHomeDir, &dwBufLen ); RegCloseKey( hKey ); if (( res != ERROR_SUCCESS ) || ( dwBufLen > MAX_PATH_BYTES )) { perror ( "Cannot read the Aimsun home directory from registry" ); return; } /* Concatenate the home path with the executable name. */ StringCbCat ( szHomeDir, MAX_PATH_BYTES, TEXT("\\aimsun42.exe") ); /* Create the version of home path including the quotation marks. */ StringCbCopy ( szExePath, MAX_EXE_PATH, TEXT("\"") ); StringCbCat ( szExePath, MAX_EXE_PATH, szHomeDir ); StringCbCat ( szExePath, MAX_EXE_PATH, TEXT("\"") ); /* Create the path to ELS3 executable. */ StringCbCopy ( szELS3Path, MAX_PATH_BYTES, TEXT("\"") ); StringCbCat ( szELS3Path, MAX_PATH_BYTES, szBasePath ); StringCbCat ( szELS3Path, MAX_PATH_BYTES, TEXT("\\els3\\els3sample.exe\"") ); /* Aimsun scenario modification. We have to take care of items that are specified by absolute paths in the scenario file. These items include: - network path (section #NETWORK), - ELS3 Getram extension DLL (section #EXTENSIONS), - VGS Getram extension DLL (section #EXTENSIONS). We will start with constructing the new network path.*/ StringCbCopy ( szSceDir, MAX_EXE_PATH, szBasePath ); StringCbCat ( szSceDir, MAX_EXE_PATH, TEXT("\\scenarios\\zlicin_495_601") ); StringCbCopy ( szNetPath, MAX_EXE_PATH, szSceDir ); StringCbCat ( szNetPath, MAX_EXE_PATH, TEXT("\\zlicin_495_601") ); /* Our convention is that the scenario file name corresponds to the network path name. */ StringCbCopy ( szScePath, MAX_EXE_PATH, szNetPath ); StringCbCat ( szScePath, MAX_EXE_PATH, TEXT(".sce") ); /* First modification step: Replace the location of ELS3 Getram extension. Start with constructing full path to the extension DLL.*/ StringCbCopy ( szSceDllPath, MAX_EXE_PATH, szDllPath ); StringCbCat ( szSceDllPath, MAX_EXE_PATH, TEXT("\\") ); StringCbCat ( szSceDllPath, MAX_EXE_PATH, PatternsELS3[verIndex] ); /* We are not allowed to modify scenario in place (it is part of SVN snapshot and we do not want our local changes to propagate to the trunk with every commit). Therefore we need an alternative scenario name that will be constructed now. */ StringCbCopy ( szScePathA, MAX_EXE_PATH, szScePath ); StringCbCat ( szScePathA, MAX_EXE_PATH, TEXT(".a") ); /* And do the replacements. */ replace_in_scenario ( szScePath, szScePathA, TEXT("#EXTENSIONS"), PatternsELS3, 2, szSceDllPath ); /* Second modification step. Replace the location of VGS Getram extension. */ StringCbCopy ( szSceDllPath, MAX_EXE_PATH, szDllPath ); StringCbCat ( szSceDllPath, MAX_EXE_PATH, TEXT("\\") ); StringCbCat ( szSceDllPath, MAX_EXE_PATH, PatternsVGS[verIndex] ); StringCbCopy ( szScePathB, MAX_EXE_PATH, szScePath ); StringCbCat ( szScePathB, MAX_EXE_PATH, TEXT(".b") ); replace_in_scenario ( szScePathA, szScePathB, TEXT("#EXTENSIONS"), PatternsVGS, 2, szSceDllPath ); const TCHAR * pattern_net[] = { TEXT("zlicin_495_601") }; /* Third modification step. Replace the network path. */ StringCbCopy ( szScePathC, MAX_EXE_PATH, szScePath ); StringCbCat ( szScePathC, MAX_EXE_PATH, TEXT(".c") ); replace_in_scenario ( szScePathB, szScePathC, TEXT("#NETWORK"), pattern_net, 1, szNetPath ); /* Fourth modification step. Replace the `stop time` field of RunTime information. */ replace_stoptime ( szNetPath, stopTime.c_str() ); //??? //StringCbCat ( szScePath, MAX_EXE_PATH, TEXT("\"") ); //StringCbCat ( szScePath, MAX_EXE_PATH, TEXT("\"") ); /* Spawn the process. */ hPid = _tspawnl ( _P_NOWAIT, szHomeDir, szExePath, TEXT("-run"), szScePathC, NULL ); if ( hPid < 0 ) { perror ( "Cannot start Aimsun by _spawnl()" ); return; } /* Now that Aimsun is running, we need to start also the controllers. */ for ( int c = 0 ; c < NUM_CONTROLLERS ; c++ ) { TCHAR szELS3Config[MAX_PATH]; szELS3Config[0] = 0; StringCbCat ( szELS3Config, MAX_PATH_BYTES, TEXT("\"") ); StringCbCat ( szELS3Config, MAX_PATH_BYTES, szBasePath ); StringCbCat ( szELS3Config, MAX_PATH_BYTES, TEXT("\\els3\\configs\\") ); StringCbCat ( szELS3Config, MAX_PATH_BYTES, ControllerList[c] ); StringCbCat ( szELS3Config, MAX_PATH_BYTES, TEXT(".ini\"") ); /* Create the command line for the controller. */ szExePath[0] = 0; StringCbCat ( szExePath, MAX_EXE_PATH, TEXT("start \"") ); StringCbCat ( szExePath, MAX_EXE_PATH, WindowStr[c] ); StringCbCat ( szExePath, MAX_EXE_PATH, TEXT("\" ") ); StringCbCat ( szExePath, MAX_EXE_PATH, szELS3Path ); StringCbCat ( szExePath, MAX_EXE_PATH, TEXT(" -l frm,det,spl") ); StringCbCat ( szExePath, MAX_EXE_PATH, TEXT(" -c 12.12.2007 00:00:00") ); StringCbCat ( szExePath, MAX_EXE_PATH, TEXT(" -f ") ); StringCbCat ( szExePath, MAX_EXE_PATH, szELS3Config ); /* Spawn the process. */ _tsystem ( szExePath ); } /* Aaaaggggrrrrrhhhh! C++ is much much faster than Matlab and we have a synchronisation issue with els3 controllers starting too late. */ Sleep ( 5000 ); /* Prepare full path for vehicle input data. */ StringCbCopy ( szEntrancePath, MAX_EXE_PATH, szSceDir ); StringCbCat ( szEntrancePath, MAX_EXE_PATH, TEXT("\\") ); StringCbCat ( szEntrancePath, MAX_EXE_PATH, entranceFileName.c_str() ); /* Initialise and start the vehicle input. The function call refers to delay-loaded DLL, so expect possible complainst in case that the DLL canot be found in PATH. */ initVGS ( HEADWAY_UNIFORM, szEntrancePath, TEXT("sect_stats.csv"), TEXT("glob_stats.csv") ); } void AimsunDS::initVGS ( const vgs_headway_mode headway, const string &entrancePath, const string §ionStatsPath, const string &globalStatsPath ) { int res; /* Pass information about entrance sections to VGS API (and so to the GetramVGS extension that will be generating vehicles for these entrances). */ vgs_set_entrance_sections ( EntranceSections, NumEntranceSections ); /* Check the validity of the headway mode. */ res = vgs_is_valid_headway_mode ( headway ); if ( !res ) { fprintf ( stderr, "Invalid headway mode '%d'\n", headway ); exit ( -1 ); } /* Specify where to find the CSV with vehicle entrances and which headway mode to use for generating vehicle flows. */ vgs_generate_vehicles ( entrancePath.c_str(), headway ); /* Pass to VGS API information about section identifiers we want to collect statistics for (and VGS will pass it to the GetramVGS extension). */ vgs_set_stats_sections ( StatIds, NumStatIds ); /* Specify where to store CSV files with statistical data. */ vgs_set_stats_file_names ( sectionStatsPath.c_str(), globalStatsPath.c_str() ); /* Specify the maximum queuing speed in kmph. */ vgs_set_max_qspeed ( 3.6f ); /* Unlock the semaphore blocking Aimsun from execution. */ vgs_unlock_aimsun(); } void AimsunDS::getdata ( vec &dt ) const { /* Return the last data. */ printf ( "\n\tAimsunDS::getdata() starting\n" ); if ( p_rsp == NULL ) { fprintf ( stderr, "ERROR: AimsunDS::getdata() - no data available yet!\n" ); return; } /* Loop over all controllers in the measurement set. */ for ( int i = 0; i < p_rsp->count; i++) { det_stats * ds_ptr = p_rsp->dets + i; sp_stats * sp_ptr = p_rsp->sps + i; printf ( "\tRealised signal plans:\n" ); /* Loop over all measurement sets. We will deliver just the last one. */ for ( int j = 0; j < sp_ptr->count ; j++ ) { sp_stattab * dat_ptr = sp_ptr->splans + j; int pos = _search_index ( IntersectionIDs, NumIntersections, dat_ptr->its_id ); int offset = SignalPlanOffsets[pos]; printf ( "\t[%3d] t=%6ds ", j, dat_ptr->global_time ); /* Copy particular signals. */ for ( int k = 0 ; k < dat_ptr->num_siggroups ; k++ ) { dt[k+offset] = dat_ptr->green_time[k]; printf ( "%3d ", dat_ptr->green_time[k] ); } printf ( "\n" ); } printf ( "\n\tDetector data:\n" ); /* Loop over all mesurement records with intensities and occupancies. */ for ( int j = 0 ; j < ds_ptr->count ; j++ ) { det_aggreg_table * dat_ptr = ds_ptr->stats + j; int pos = _search_index ( IntersectionIDs, NumIntersections, dat_ptr->its_id ); int moffset = MeasurementOffsets[pos]; printf ( "\t[%3d] t=%6ds ", j, dat_ptr->global_time ); for ( int k = 0 ; k < dat_ptr->num_det ; k++ ) { dt[2*k+moffset] = dat_ptr->dt_intensity[k]; dt[2*k+moffset+1] = dat_ptr->dt_occupancy[k]; printf ( "{%2d/%2d} ", dat_ptr->dt_intensity[k], dat_ptr->dt_occupancy[k] ); } printf ( "\n" ); } } printf ( "\tAimsunDS::getdata() finished\n" ); cout << dt ; } void AimsunDS::write ( vec &ut ) { eh_res sp_res; /* Result code from write operation. */ eh_hrdels3 hrdsp[2]; /* Signal plan for two intersections. */ /* Check that the length of the vector is correct. */ if ( ut.size() != utsize ) { fprintf ( stderr, "Incorrect length of data vector (should be %d)", utsize ); exit(-1); } /* Check that the phase lengths sum to correct cycle time. */ /* if ( ut[0]+ut[1]+ut[2] != 80 ) { fprintf ( stderr, "Intersection 495: F1+F2+F3 != Tc (should be 80)" ); exit(-1); } if ( ut[3]+ut[4]+ut[5] != 80 ) { fprintf ( stderr, "Intersection 601: F1+F2+F3 != Tc (should be 80)" ); exit(-1); }*/ hrdsp[0].id = 495; hrdsp[0].sp.cycle_time = int ( ut[0] ); hrdsp[0].sp.offset = int ( ut[1] ); hrdsp[0].sp.phase_len[0] = int ( ut[2] ); hrdsp[0].sp.phase_len[1] = int ( ut[3] ); hrdsp[0].sp.phase_len[2] = int ( ut[4] ); hrdsp[0].sp.phase_len[3] = -1; hrdsp[1].id = 601; hrdsp[1].sp.cycle_time = int ( ut[ 0] ); hrdsp[1].sp.offset = int ( ut[ 8] ); hrdsp[1].sp.phase_len[0] = int ( ut[ 9] ); hrdsp[1].sp.phase_len[1] = int ( ut[10] ); hrdsp[1].sp.phase_len[2] = int ( ut[11] ); hrdsp[1].sp.phase_len[3] = -1; /* Write new signal plan to the shared memory of active controllers. */ sp_res = eh_write_hrd_data( hrdsp, 2 ); if ( sp_res != EH_RES_OK ) { printf ( "eh_write_hrd_data(): " ); if ( sp_res == EH_RES_WCNT ) { printf ( "Wrong count of ELS3...\n" ); } else if ( sp_res == EH_RES_WRID ) { printf ( "Wrong intersection ID...\n" ); } else { printf ( "Unknown error [%d] ...\n", sp_res ); } } } void AimsunDS::step() { eh_wres wsp_res; /* Result code of the wait operation. */ int count; /* Count of measurement sets. */ /* Wait for 100 seconds until data from all ELS3 controllers arrive. */ wsp_res = eh_wait_on_rsp_complete( 100000 ); if ( wsp_res != EH_WRES_OK ) { printf ( "eh_wait_on_rsp_complete(): " ); if ( wsp_res == EH_WRES_TIMEOUT ) { printf ( "Timeout elapsed ...\n" ); } else if ( wsp_res == EH_WRES_EXIT ) { printf ( "Some ELS3 has exited ...\n" ); } else { printf ( "Unknown error [%d] ...\n", wsp_res ); } exit(-1); } /* This will copy the detector data out. It is needed by the Matlab HRDS version due to its internal structure - realised signal plans are read asynchronously and later. The value of `count` reports the number of data sets that will be returned by the call to `eh_read_realised_sp()`. This coount will be at least 1 but it may be higher in cases where the controller reports measurements in short intervals but we are reading them less frequently. */ count = eh_copy_out_measurements (); printf ( "got %d measurements from controllers ...", count ); /* Read realised signal plan from shared memory. */ if ( eh_read_realised_sp( &p_rsp ) == EH_RES_OK ) { printf("\nsuccesfully read data from controllers\n" ); } else { printf(" but got no data ???\n"); } }