├── LICENSE ├── README.md ├── racer ├── .cproject ├── .gitignore ├── .project ├── .settings │ ├── ch.hsr.ifs.sconsolidator.core.prefs │ └── org.eclipse.cdt.codan.core.prefs ├── SConstruct ├── include │ ├── basic_size_header_socket.h │ ├── echo_server.h │ ├── gmp_socket.h │ ├── http_request.h │ ├── http_timing_client.h │ ├── nop_echo_server.h │ ├── raw_socket.h │ ├── string_split.h │ ├── strings.h │ ├── time_helper.h │ ├── timing_client.h │ └── x_runtime_client.h └── src │ ├── bin │ ├── SConscript │ ├── run_echo_server.cpp │ ├── run_http_client.cpp │ ├── run_nop_echo_server.cpp │ ├── run_timing_client.cpp │ ├── run_x_runtime_client.cpp │ └── send_http_request.py │ ├── clients │ ├── SConscript │ ├── http_timing_client.cpp │ ├── timing_client.cpp │ └── x_runtime_client.cpp │ ├── servers │ ├── SConscript │ ├── echo_server.cpp │ └── nop_echo_server.cpp │ ├── socket │ ├── SConscript │ ├── basic_size_header_socket.cpp │ └── raw_socket.cpp │ └── utils │ ├── SConscript │ ├── http_request.cpp │ ├── string_split.cpp │ ├── strings.cpp │ └── time_helper.cpp └── time_trial_gui ├── .DS_Store ├── .gitignore ├── gui ├── __init__.py ├── data_source_model.py ├── experiment_combo_box.py ├── experiments_tab.py ├── feasibility_tab.py ├── histogram.py ├── http_request_text_edit.py ├── mpl_canvas.py ├── new_trial_dialog.py ├── plot_settings_dialog.py ├── plot_style_edit_dialog.py ├── plotter_tab.py ├── plotter_widget.py ├── racer_edit_dialog.py ├── settings_tab.py ├── sqlalchemy_table_model.py └── trial_detail_widget.py ├── images ├── .DS_Store └── clock.png ├── lib ├── __init__.py ├── base.py ├── box_test.py ├── filters.py ├── plot.py ├── plot_settings.py ├── racer_driver.py ├── rq_result_processor.py ├── timing_data.py └── trial_jobs.py ├── models ├── __init__.py ├── experiment.py ├── racer.py └── trial.py ├── requirements.txt └── time_trial.py /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Daniel A. Mayer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Time Trial GUI 2 | The source code for the time trial GUI is in the `time_trial` folder. 3 | 4 | ## Requirements 5 | 6 | * Python 3.0 7 | * The packages listed in the `requirements.txt` file which can be installed via `pip3 install -r requirements.txt` 8 | * A locally running redis server. 9 | 10 | 11 | ## Running time_trial 12 | 13 | * Execute `time_trial.py`. 14 | 15 | 16 | # Racer 17 | The host executing the trials must have both the C++ code and the time trial Python code installed. The Python code is required in order to connect to the redis server and drive the execution of the trial. Since the relative path to the C++ binaries is currently hard-coded in the Python code, the folder structure should not be changed. Linux is the recommended operating system (tesing performed using Ubuntu 14.04 Server LTS). 18 | 19 | The source code for the C++ component is in the `racer` folder. 20 | 21 | ## Requirements 22 | The following packages are required in order to compile the racer code: 23 | * build-essential 24 | * scons 25 | * libgmp-dev 26 | * libcurl4-dev 27 | * libcurl4-openssl-dev 28 | * libcppnetlib-dev 29 | 30 | On Ubuntu, install all packages via: 31 | `sudo apt-get install build-essential scons libgmp-dev libcurl4-openssl-dev libcppnetlib-dev` 32 | 33 | 34 | ## Compiling the source 35 | The racer C++ code is built using the SCons build system. When all required packages are installed, it should be sufficient to call `scons` in the `racer` folder to build all components. The binaries are created in the the `racer/bin/` folder. There is no need to direclty execute the binary. The racer driver will rake care of that automatically via the `rqworker` (see below), 36 | 37 | 38 | # Executing Trials 39 | On the host running the Time Trial GUI: 40 | * Use the time_trial GUI to configure your racers. Note that the hostname will be used to later execute trials on a racer host. 41 | * Configure your trial in the first tab. 42 | * Click the "Start" button in order to schedule it. 43 | 44 | * SSH into your racer and forward your local redis port along: 45 | `ssh -R 6379:localhost:6379 ` 46 | * Change into the `time_trial` folder. 47 | * Execute `rqworker ` where `` is the hostname specified for the racer within the GUI. 48 | 49 | At this point, the `rqworker` will connect to redis (via the SSH tunnel), pull a job for the named racer, and execute it. Once complete, the results will populate the time trial UI (this may require a refresh). 50 | 51 | From here, a feasibility analysis can be performed by right clicking an individual trial in the table view and selecting either "shorter trial" or "longer trial". The choice should correspond to the two different timings one expects in the experiment. When switching to the "Feasibility Analysis" tab, two plots should be visible that can be customized. In additon, the box test can be executed using variable parameters. 52 | 53 | 54 | # Automated Attacks 55 | To come. 56 | 57 | # Disclaimer 58 | The source code was developed with a focus on the required functionality. 59 | Security considerations were not a priority and thus untrusted access to 60 | time_trial and the racer should be restricted. 61 | -------------------------------------------------------------------------------- /racer/.cproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | debug 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /racer/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *.swp 4 | .sconsign.dblite 5 | lib/ 6 | bin/ 7 | -------------------------------------------------------------------------------- /racer/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | cpp 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder 10 | full,incremental, 11 | 12 | 13 | 14 | 15 | ch.hsr.ifs.sconsolidator.Builder 16 | 17 | 18 | 19 | 20 | 21 | org.eclipse.cdt.core.cnature 22 | org.eclipse.cdt.core.ccnature 23 | org.eclipse.cdt.managedbuilder.core.managedBuildNature 24 | org.eclipse.cdt.managedbuilder.core.ScannerConfigNature 25 | ch.hsr.ifs.sconsolidator.ExistingCodeNature 26 | 27 | 28 | -------------------------------------------------------------------------------- /racer/.settings/ch.hsr.ifs.sconsolidator.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | perfAccuracyProfile=default 3 | -------------------------------------------------------------------------------- /racer/.settings/org.eclipse.cdt.codan.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.cdt.codan.checkers.errnoreturn=Warning 3 | org.eclipse.cdt.codan.checkers.errnoreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false} 4 | org.eclipse.cdt.codan.checkers.errreturnvalue=Error 5 | org.eclipse.cdt.codan.checkers.errreturnvalue.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 6 | org.eclipse.cdt.codan.checkers.noreturn=Error 7 | org.eclipse.cdt.codan.checkers.noreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false} 8 | org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation=Error 9 | org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 10 | org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem=Error 11 | org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 12 | org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem=Warning 13 | org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 14 | org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem=Error 15 | org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 16 | org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem=Warning 17 | org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},no_break_comment\=>"no break",last_case_param\=>false,empty_case_param\=>false} 18 | org.eclipse.cdt.codan.internal.checkers.CatchByReference=Warning 19 | org.eclipse.cdt.codan.internal.checkers.CatchByReference.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},unknown\=>false,exceptions\=>()} 20 | org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem=Error 21 | org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 22 | org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization=Warning 23 | org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},skip\=>true} 24 | org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem=Error 25 | org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 26 | org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem=Error 27 | org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 28 | org.eclipse.cdt.codan.internal.checkers.InvalidArguments=Error 29 | org.eclipse.cdt.codan.internal.checkers.InvalidArguments.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 30 | org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem=Error 31 | org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 32 | org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem=Error 33 | org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 34 | org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem=Error 35 | org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 36 | org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem=Error 37 | org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 38 | org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker=-Info 39 | org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},pattern\=>"^[a-z]",macro\=>true,exceptions\=>()} 40 | org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=Warning 41 | org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 42 | org.eclipse.cdt.codan.internal.checkers.OverloadProblem=Error 43 | org.eclipse.cdt.codan.internal.checkers.OverloadProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 44 | org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem=Error 45 | org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 46 | org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem=Error 47 | org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 48 | org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem=-Warning 49 | org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 50 | org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem=-Warning 51 | org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 52 | org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem=Warning 53 | org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>()} 54 | org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem=Warning 55 | org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},paramNot\=>false} 56 | org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem=Warning 57 | org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},else\=>false,afterelse\=>false} 58 | org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem=Error 59 | org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 60 | org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem=Warning 61 | org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true} 62 | org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem=Warning 63 | org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true} 64 | org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem=Warning 65 | org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>("@(\#)","$Id")} 66 | org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem=Error 67 | org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} 68 | -------------------------------------------------------------------------------- /racer/SConstruct: -------------------------------------------------------------------------------- 1 | import os 2 | if ARGUMENTS.get('debug', 0): 3 | ccflags = '-g -D DEBUG' 4 | else: 5 | ccflags = '-O3 -march=core2 -mssse3 -finline -funroll-loops ' 6 | # ccflags = '-O3 -march=core2 -mssse3 -mfpmath=sse -finline -funroll-loops ' 7 | 8 | env = Environment(CC = 'g++', 9 | CPPPATH = ['.', os.path.abspath('include/')], 10 | CCFLAGS = ccflags ) 11 | 12 | 13 | env.SConsignFile() 14 | 15 | subdirs = [ 16 | 'socket', 17 | 'servers', 18 | 'clients', 19 | 'utils' 20 | ] 21 | 22 | # build all the libraries 23 | Export('env') 24 | for dir in subdirs: 25 | SConscript( os.path.join('src', dir, 'SConscript'), exports = ['env']) 26 | 27 | 28 | # then build the binaries 29 | binenv = env.Clone() 30 | binenv.Append(LIBPATH=['#lib']) 31 | SConscript( 'src/bin/SConscript', exports = ['binenv']) 32 | -------------------------------------------------------------------------------- /racer/include/basic_size_header_socket.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BasicSizeHeaderSocket.hpp 3 | * 4 | * Created on: Oct 14, 2009 5 | * Author: mayer 6 | */ 7 | 8 | #ifndef BASICSIZEHEADERSOCKET_HPP_ 9 | #define BASICSIZEHEADERSOCKET_HPP_ 10 | 11 | #include "raw_socket.h" 12 | #include "gmp_socket.h" 13 | #include 14 | 15 | class BasicSizeHeaderSocket: public RawSocket, public GMPCapableSocket { 16 | public: 17 | BasicSizeHeaderSocket(); 18 | virtual ~BasicSizeHeaderSocket(); 19 | 20 | void send(const set&);//new 21 | void send(const vector&);//new 22 | void send(const string&); 23 | void send(const mpz_class&); 24 | void sendInteger(const int); 25 | 26 | set& receive(set&);//new 27 | vector& receive(vector&);//new 28 | string& receive(string&); 29 | mpz_class& receive(mpz_class&); 30 | int& receiveInteger(int&); 31 | }; 32 | 33 | #endif /* BasicSizeHeaderSocket_HPP_ */ 34 | -------------------------------------------------------------------------------- /racer/include/echo_server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * EchoServer.h 3 | * 4 | * Created on: Mar 23, 2014 5 | * Author: mayer 6 | */ 7 | 8 | #ifndef ECHOSERVER_H_ 9 | #define ECHOSERVER_H_ 10 | 11 | #include "basic_size_header_socket.h" 12 | #include 13 | 14 | 15 | class EchoServer { 16 | public: 17 | EchoServer(int); 18 | virtual ~EchoServer(); 19 | 20 | void loop(); 21 | 22 | 23 | private: 24 | BasicSizeHeaderSocket* socket; 25 | }; 26 | 27 | #endif /* ECHOSERVER_H_ */ 28 | -------------------------------------------------------------------------------- /racer/include/gmp_socket.h: -------------------------------------------------------------------------------- 1 | /* 2 | * GMPCapableSocket.hpp 3 | * 4 | * Created on: Apr 17, 2010 5 | * Author: mayer 6 | */ 7 | 8 | #ifndef GMPCAPABLESOCKET_HPP_ 9 | #define GMPCAPABLESOCKET_HPP_ 10 | 11 | #include 12 | #include 13 | 14 | 15 | class GMPCapableSocket { 16 | 17 | public: 18 | virtual void send(const std::set&) = 0; //new 19 | virtual void send(const std::vector&) = 0; //new 20 | virtual void send(const std::string&) = 0; 21 | virtual void send(const mpz_class&) = 0; 22 | virtual void sendInteger(const int) = 0; 23 | 24 | virtual std::set& receive(std::set&) = 0;//new 25 | virtual std::vector& receive(std::vector&) = 0;//new 26 | virtual std::string& receive(std::string&) = 0; 27 | virtual mpz_class& receive(mpz_class&) = 0; 28 | virtual int& receiveInteger(int&) = 0; 29 | 30 | }; 31 | 32 | #endif /* GMPCAPABLESOCKET_HPP_ */ 33 | -------------------------------------------------------------------------------- /racer/include/http_request.h: -------------------------------------------------------------------------------- 1 | #include 2 | #define BOOST_NETWORK_ENABLE_HTTPS 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifndef HTTREQUEST_H_ 11 | #define HTTPREQUEST_H_ 12 | 13 | using namespace boost::network::http; 14 | 15 | // use http_keepalive_8bit_tcp_resolve so we use a single tcp connection to the server. 16 | typedef basic_client http11_client; 17 | typedef basic_client http10_client; 18 | 19 | class HttpRequest { 20 | public: 21 | virtual ~HttpRequest() {}; 22 | 23 | virtual void get() = 0; 24 | virtual void head() = 0; 25 | virtual void post() = 0; 26 | virtual void put() = 0; 27 | virtual void delete_() = 0; 28 | virtual void execute() = 0; 29 | 30 | virtual uint16_t get_response_code() = 0; 31 | virtual std::string get_response_body() = 0; 32 | }; 33 | 34 | class Http11Request: public HttpRequest { 35 | public: 36 | Http11Request(std::string &url, 37 | std::string &verb, 38 | std::string &payload, 39 | std::vector &headers); 40 | ~Http11Request(); 41 | 42 | virtual void get(); 43 | virtual void head(); 44 | virtual void post(); 45 | virtual void put(); 46 | virtual void delete_(); 47 | virtual void execute(); 48 | 49 | virtual uint16_t get_response_code(); 50 | virtual std::string get_response_body(); 51 | 52 | private: 53 | http11_client client_; 54 | http11_client::request request_; 55 | http11_client::response response_; 56 | void (Http11Request::*verb_)(); 57 | }; 58 | 59 | class Http10Request: public HttpRequest { 60 | public: 61 | Http10Request(std::string &url, 62 | std::string &verb, 63 | std::string &payload, 64 | std::vector &headers); 65 | ~Http10Request(); 66 | 67 | virtual void get(); 68 | virtual void head(); 69 | virtual void post(); 70 | virtual void put(); 71 | virtual void delete_(); 72 | virtual void execute(); 73 | 74 | virtual uint16_t get_response_code(); 75 | virtual std::string get_response_body(); 76 | 77 | private: 78 | http10_client client_; 79 | http10_client::request request_; 80 | http10_client::response response_; 81 | void (Http10Request::*verb_)(); 82 | }; 83 | 84 | 85 | // thrown when client attempts to instantiate a request 86 | // with an invalid verb, http version, or malformed header. 87 | class InvalidHttpVerb: public std::exception {}; 88 | class InvalidHttpVersion: public std::exception {}; 89 | class InvalidHttpHeader: public std::exception {}; 90 | 91 | HttpRequest *make_request_for_version(std::string http_version, 92 | std::string url, 93 | std::string verb, 94 | std::string payload, 95 | std::vector headers); 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /racer/include/http_timing_client.h: -------------------------------------------------------------------------------- 1 | /* 2 | * HTTPTimingClient.h 3 | * 4 | * Created on: Mar 23, 2014 5 | * Author: mayer 6 | */ 7 | 8 | #ifndef HTTPTIMINGCLIENT_H_ 9 | #define HTTPTIMINGCLIENT_H_ 10 | 11 | #include "basic_size_header_socket.h" 12 | #include "string.h" 13 | #include 14 | #include "time_helper.h" 15 | #include "http_request.h" 16 | #include 17 | 18 | class HTTPTimingClient { 19 | public: 20 | HTTPTimingClient(std::string &url, 21 | std::string &verb, 22 | std::string &http_version, 23 | std::string &payload, 24 | vector &headers); 25 | virtual ~HTTPTimingClient(); 26 | 27 | vector & run(long reps); 28 | 29 | private: 30 | HttpRequest *request_; 31 | }; 32 | 33 | #endif /* HTTPTIMINGCLIENT_H_ */ 34 | -------------------------------------------------------------------------------- /racer/include/nop_echo_server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * EchoServer.h 3 | * 4 | * Created on: Mar 23, 2014 5 | * Author: mayer 6 | */ 7 | 8 | #ifndef NOPECHOSERVER_H_ 9 | #define NOPECHOSERVER_H_ 10 | 11 | #include "basic_size_header_socket.h" 12 | #include 13 | 14 | 15 | class NopEchoServer { 16 | public: 17 | NopEchoServer(int, double); 18 | virtual ~NopEchoServer(); 19 | void sleep_for(unsigned long ); 20 | 21 | void loop(); 22 | 23 | 24 | private: 25 | BasicSizeHeaderSocket* socket; 26 | double frequency_in_ghz; 27 | }; 28 | 29 | #endif /* NOPECHOSERVER_H_ */ 30 | -------------------------------------------------------------------------------- /racer/include/raw_socket.h: -------------------------------------------------------------------------------- 1 | /* 2 | * RawSockets.hpp 3 | * 4 | * Created on: Aug 31, 2009 5 | * Author: mayer 6 | */ 7 | 8 | #ifndef RAWSOCKET_HPP_ 9 | #define RAWSOCKET_HPP_ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | using namespace std; 27 | 28 | 29 | class RawSocket { 30 | public: 31 | RawSocket(); 32 | 33 | void setupServer(unsigned int); 34 | 35 | void setupClient(string, unsigned int); 36 | 37 | void closeSocket(); 38 | 39 | void sendString(const string&); 40 | void sendChars(const char*); 41 | void sendInteger(const int); 42 | void sendLong(const long); 43 | void sendData(const void*, unsigned long); 44 | 45 | string& receiveString(string&, unsigned long); 46 | void* receiveData(void*, unsigned long); 47 | int& receiveInteger(int&); 48 | long& receiveLong(long&); 49 | 50 | virtual ~RawSocket(); 51 | 52 | private: 53 | // socket for server and client to write to 54 | int socketFd; 55 | 56 | // File descriptor for server socket. 57 | // The actual socket before binding takes place 58 | int serverSocketFd; 59 | 60 | }; 61 | 62 | #endif /* RAWSOCKET_HPP_ */ 63 | -------------------------------------------------------------------------------- /racer/include/string_split.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | std::vector &split(const std::string &s, char delim, std::vector &elems, unsigned int count); 8 | std::vector split(const std::string &s, char delim, unsigned int count); 9 | -------------------------------------------------------------------------------- /racer/include/strings.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | std::vector &split(const std::string &s, char delim, std::vector &elems, unsigned int count); 8 | std::vector split(const std::string &s, char delim, unsigned int count); 9 | bool get_first_capture(const boost::regex &ex, const std::string st, std::string &capture); 10 | -------------------------------------------------------------------------------- /racer/include/time_helper.h: -------------------------------------------------------------------------------- 1 | #ifndef _TIME_HELPER_HPP 2 | #define _TIME_HELPER_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include /* clock_t, clock, CLOCKS_PER_SEC */ 12 | 13 | #ifdef __MACH__ 14 | #include 15 | #include 16 | #endif 17 | 18 | 19 | using namespace std; 20 | 21 | /* Wrapper class for storing 3 long time values */ 22 | class TimeMark 23 | { 24 | public: 25 | unsigned long utime; 26 | unsigned long stime; 27 | unsigned long wtime; 28 | clock_t ticks; 29 | timespec clock_time; 30 | string name; 31 | }; 32 | 33 | 34 | class TimeHelper 35 | { 36 | 37 | private: 38 | /* Name to identify this timer */ 39 | string tag; 40 | vector marks; 41 | /* Structs to store start and marked times */ 42 | struct tms start_times; 43 | struct tms mark_times; 44 | clock_t start_ticks; 45 | /* time_ts to store wall times values */ 46 | time_t start_wall; 47 | time_t mark_wall; 48 | clock_t mark_ticks; 49 | 50 | void current_utc_time(struct timespec *ts); 51 | 52 | 53 | /* flag to keep track of timer state ( 0 = unitialized, 1 = active, 2 = active and marked) */ 54 | char active; 55 | 56 | timespec start_clock_time; 57 | 58 | 59 | /* private functions for converting stored structs into times to be stored */ 60 | unsigned long do_utime(); 61 | unsigned long do_stime(); 62 | unsigned long do_wtime(); 63 | unsigned long do_ticks(); 64 | public: 65 | /* Constructor*/ 66 | TimeHelper(); 67 | /* Constructor: takes a string to identify timer by */ 68 | TimeHelper(string); 69 | /* Starts the timer */ 70 | void start(); 71 | /* Marks the timer's current time. (Think of stop time, except the timer doesn't stop counting) */ 72 | void mark(); 73 | /* Marks the timer's current time and stores it under the given name */ 74 | void mark(string); 75 | 76 | 77 | /* Get functions to retrieve the elapsed time between starting the timer and the most recent mark*/ 78 | unsigned long utime(); 79 | unsigned long utime(int); 80 | unsigned long stime(); 81 | unsigned long stime(int); 82 | unsigned long wtime(); 83 | unsigned long wtime(int); 84 | /* String return function to print marked timer data*/ 85 | string str(); 86 | string str(int); 87 | /* Get functions to retrieve entire vector, an index, or the most recent */ 88 | vector getVec(); 89 | vector getDiffVec(); 90 | TimeMark getMark(int); 91 | TimeMark getMark(); 92 | }; 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /racer/include/timing_client.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TimingClient.h 3 | * 4 | * Created on: Mar 23, 2014 5 | * Author: mayer 6 | */ 7 | 8 | #ifndef TIMINGCLIENT_H_ 9 | #define TIMINGCLIENT_H_ 10 | 11 | #include "basic_size_header_socket.h" 12 | #include "string.h" 13 | #include 14 | #include "time_helper.h" 15 | 16 | class TimingClient { 17 | public: 18 | TimingClient(string, int); 19 | virtual ~TimingClient(); 20 | 21 | vector & run(int delay, int reps); 22 | 23 | private: 24 | BasicSizeHeaderSocket* socket; 25 | }; 26 | 27 | #endif /* TIMINGCLIENT_H_ */ 28 | -------------------------------------------------------------------------------- /racer/include/x_runtime_client.h: -------------------------------------------------------------------------------- 1 | /* 2 | * HTTPTimingClient.h 3 | * 4 | * Created on: Mar 23, 2014 5 | * Author: mayer 6 | */ 7 | 8 | #ifndef XRUNTIMECLIENT_H_ 9 | #define XRUNTIMECLIENT_H_ 10 | 11 | #include "basic_size_header_socket.h" 12 | #include "string.h" 13 | #include 14 | #include "time_helper.h" 15 | #include "http_request.h" 16 | #include 17 | 18 | class XRuntimeClient { 19 | public: 20 | XRuntimeClient(std::string &url, 21 | std::string &verb, 22 | std::string &http_version, 23 | std::string &payload, 24 | vector &headers); 25 | virtual ~XRuntimeClient(); 26 | 27 | vector & run(long reps); 28 | 29 | private: 30 | HttpRequest *request_; 31 | }; 32 | 33 | #endif /* XRUNTIMECLIENT_H_ */ 34 | -------------------------------------------------------------------------------- /racer/src/bin/SConscript: -------------------------------------------------------------------------------- 1 | Import('binenv') 2 | 3 | binenv.Program('#bin/run_nop_echo_server', 4 | Split("run_nop_echo_server.cpp"), 5 | LIBS=['gmp', 'gmpxx', 'server', 'socket', 'gmp', 'gmpxx' ]) 6 | 7 | binenv.Program('#bin/run_echo_server', 8 | Split("run_echo_server.cpp"), 9 | LIBS=['gmp', 'gmpxx', 'server', 'socket', 'gmp', 'gmpxx' ]) 10 | 11 | binenv.Program('#bin/run_timing_client', 12 | Split("run_timing_client.cpp"), 13 | LIBS=['gmp', 'gmpxx', 'server', 'socket', 'client', 'socket', 'utils', 'gmp', 'gmpxx' ]) 14 | 15 | binenv.Program('#bin/run_http_timing_client', 16 | Split("run_http_client.cpp"), 17 | LIBS=['gmp', 'gmpxx', 'curl', 'server', 'socket', 'client', 'utils', 18 | 'boost_system', 'boost_thread', 'boost_date_time', 'pthread', 'cppnetlib-uri', 'ssl', 'crypt', 'crypto', 'cppnetlib-client-connections']) 19 | -------------------------------------------------------------------------------- /racer/src/bin/run_echo_server.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * run_echo_server.cpp 3 | * 4 | * Created on: Mar 23, 2014 5 | * Author: mayer 6 | */ 7 | #include "echo_server.h" 8 | 9 | int main(int argc, char** argv) { 10 | int port = 7147; 11 | EchoServer s(port); 12 | s.loop(); 13 | } 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /racer/src/bin/run_http_client.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * run_http_timing_client.cpp 3 | * 4 | * Created on: May 18, 2014 5 | * Author: jsandin 6 | */ 7 | 8 | #include 9 | #include "http_timing_client.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | void display_times(vector & times) { 16 | for(unsigned int i = 0; i < times.size(); i++) { 17 | cout << times[i].clock_time.tv_sec << "s;" << times[i].clock_time.tv_nsec << "ns" << endl; 18 | } 19 | } 20 | 21 | int set_process_priority(int priority) { 22 | int which = PRIO_PROCESS; 23 | id_t pid; 24 | int ret; 25 | 26 | pid = getpid(); 27 | ret = setpriority(which, pid, priority); 28 | return ret; 29 | } 30 | 31 | void enable_real_time() { 32 | set_process_priority(PRIO_MIN); 33 | } 34 | 35 | void set_cpu_affinity(int cpu) { 36 | cpu_set_t mask; 37 | CPU_ZERO(&mask); 38 | CPU_SET(cpu, &mask); 39 | printf("CPU affinity return code: %d\n", sched_setaffinity(0, sizeof(mask), &mask)); 40 | } 41 | 42 | int main(int argc, char* argv[]) { 43 | if (argc < 8) { 44 | cout << "./run_http_timing_client url verb http_version payload real_time? cpu_id delay reps [header1:value1 header2:value2 ...]" << endl; 45 | exit(1); 46 | } 47 | 48 | string url = string(argv[1]); 49 | string verb = string(argv[2]); 50 | string http_version = string(argv[3]); 51 | string payload = string(argv[4]); 52 | 53 | bool real_time = false; 54 | if(string(argv[5]) == "1") { 55 | real_time = true; 56 | enable_real_time(); 57 | } 58 | 59 | int cpuid = atoi(argv[6]); 60 | set_cpu_affinity(cpuid); 61 | long delay = atol(argv[7]); 62 | long reps = atol(argv[8]); 63 | 64 | vector headers; 65 | for(int i = 9; i < argc; i++) { 66 | headers.push_back(argv[i]); 67 | } 68 | 69 | cout << "Sending request to: " << url << endl; 70 | cout << "Reps" << ":" << reps << endl; 71 | cout << "Delay" << ":" << delay << endl; 72 | cout << "CPU" << ":" << cpuid << endl; 73 | cout << "Real-Time" << ":" << real_time << endl; 74 | 75 | HTTPTimingClient t = HTTPTimingClient(url, verb, http_version, payload, headers); 76 | vector times = t.run(reps); 77 | display_times(times); 78 | } 79 | -------------------------------------------------------------------------------- /racer/src/bin/run_nop_echo_server.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * run_echo_server.cpp 3 | * 4 | * Created on: Mar 23, 2014 5 | * Author: mayer 6 | */ 7 | #include "nop_echo_server.h" 8 | 9 | void set_cpu_affinity(int cpu) { 10 | cpu_set_t mask; 11 | CPU_ZERO(&mask); 12 | CPU_SET(cpu, &mask); 13 | printf("CPU affinity return code: %d", sched_setaffinity(0, sizeof(mask), &mask)); 14 | } 15 | 16 | 17 | int main(int argc, char** argv) { 18 | int port = 7147; 19 | double frequency_in_ghz = atof(argv[1]); 20 | printf("Frequency %f\n", frequency_in_ghz); 21 | set_cpu_affinity(1); 22 | NopEchoServer s(port,frequency_in_ghz); 23 | s.loop(); 24 | } 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /racer/src/bin/run_timing_client.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * timing_client.cpp 3 | * 4 | * Created on: Mar 23, 2014 5 | * Author: mayer 6 | */ 7 | 8 | #include 9 | #include "timing_client.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | void display_times(vector & times) { 16 | for(unsigned int i = 0; i < times.size(); i++) { 17 | cout << times[i].clock_time.tv_sec << "s;" << times[i].clock_time.tv_nsec << "ns" << endl; 18 | } 19 | } 20 | 21 | int set_process_priority(int priority) { 22 | int which = PRIO_PROCESS; 23 | id_t pid; 24 | int ret; 25 | 26 | pid = getpid(); 27 | ret = setpriority(which, pid, priority); 28 | return ret; 29 | } 30 | 31 | void enable_real_time() { 32 | set_process_priority(PRIO_MIN); 33 | } 34 | 35 | void set_cpu_affinity(int cpu) { 36 | cpu_set_t mask; 37 | CPU_ZERO(&mask); 38 | CPU_SET(cpu, &mask); 39 | printf("CPU affinity return code: %d", sched_setaffinity(0, sizeof(mask), &mask)); 40 | } 41 | 42 | 43 | 44 | int main(int argc, char* argv[]) { 45 | if (argc != 7) { 46 | cout << "./run_timing_client hostname port real_time? cpu_id delay reps" << endl; 47 | exit(1); 48 | } 49 | 50 | string hostname = string(argv[1]); 51 | int port = atoi(argv[2]); 52 | 53 | bool real_time = false; 54 | if(string(argv[3]) == "1") { 55 | real_time = true; 56 | enable_real_time(); 57 | } 58 | 59 | int cpuid = atoi(argv[4]); 60 | set_cpu_affinity(cpuid); 61 | 62 | long delay = atol(argv[5]); 63 | long reps = atol(argv[6]); 64 | 65 | cout << "Connecting to: " << hostname << ":" << port << endl; 66 | cout << "Reps" << ":" << reps << endl; 67 | cout << "Delay" << ":" << delay << endl; 68 | cout << "CPU" << ":" << cpuid << endl; 69 | cout << "Real-Time" << ":" << real_time << endl; 70 | 71 | TimingClient t(hostname, 7147); 72 | vector times = t.run(delay, reps); 73 | 74 | display_times(times); 75 | 76 | 77 | } 78 | -------------------------------------------------------------------------------- /racer/src/bin/run_x_runtime_client.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * run_http_timing_client.cpp 3 | * 4 | * Created on: May 18, 2014 5 | * Author: jsandin 6 | */ 7 | 8 | #include 9 | #include "x_runtime_client.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | void display_times(vector & times) { 17 | std::cout << std::fixed; 18 | 19 | for(unsigned int i = 0; i < times.size(); i++) { 20 | float t = atof(times[i].c_str()); 21 | long s = floor(t); 22 | long ns = (t - s) * 1000000000; 23 | 24 | cout << s << "s;"; 25 | cout << ns << "ns" << endl; 26 | } 27 | } 28 | 29 | int set_process_priority(int priority) { 30 | int which = PRIO_PROCESS; 31 | id_t pid; 32 | int ret; 33 | 34 | pid = getpid(); 35 | ret = setpriority(which, pid, priority); 36 | return ret; 37 | } 38 | 39 | void enable_real_time() { 40 | set_process_priority(PRIO_MIN); 41 | } 42 | 43 | void set_cpu_affinity(int cpu) { 44 | cpu_set_t mask; 45 | CPU_ZERO(&mask); 46 | CPU_SET(cpu, &mask); 47 | printf("CPU affinity return code: %d\n", sched_setaffinity(0, sizeof(mask), &mask)); 48 | } 49 | 50 | int main(int argc, char* argv[]) { 51 | if (argc < 8) { 52 | cout << "./run_x_runtime_client url verb http_version payload real_time? cpu_id delay reps [header1:value1 header2:value2 ...]" << endl; 53 | exit(1); 54 | } 55 | 56 | string url = string(argv[1]); 57 | string verb = string(argv[2]); 58 | string http_version = string(argv[3]); 59 | string payload = string(argv[4]); 60 | 61 | bool real_time = false; 62 | if(string(argv[5]) == "1") { 63 | real_time = true; 64 | enable_real_time(); 65 | } 66 | 67 | int cpuid = atoi(argv[6]); 68 | set_cpu_affinity(cpuid); 69 | long delay = atol(argv[7]); 70 | long reps = atol(argv[8]); 71 | 72 | vector headers; 73 | for(int i = 9; i < argc; i++) { 74 | headers.push_back(argv[i]); 75 | } 76 | 77 | cout << "Sending request to: " << url << endl; 78 | cout << "Reps" << ":" << reps << endl; 79 | cout << "Delay" << ":" << delay << endl; 80 | cout << "CPU" << ":" << cpuid << endl; 81 | cout << "Real-Time" << ":" << real_time << endl; 82 | 83 | XRuntimeClient t = XRuntimeClient(url, verb, http_version, payload, headers); 84 | vector times = t.run(reps); 85 | display_times(times); 86 | } 87 | -------------------------------------------------------------------------------- /racer/src/bin/send_http_request.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | from StringIO import StringIO 6 | from BaseHTTPServer import BaseHTTPRequestHandler 7 | 8 | CPP_HTTP_TIMING_EXECUTABLE = './run_http_timing_client' 9 | 10 | class ParseException(Exception): 11 | pass 12 | 13 | class HTTPRequest(BaseHTTPRequestHandler): 14 | def __init__(self, request_text): 15 | self.rfile = StringIO(request_text) 16 | self.raw_requestline = self.rfile.readline() 17 | self.error_code = self.error_message = None 18 | self.parse_request() 19 | self.request_body = self.rfile.read() 20 | 21 | def send_error(self, code, message): 22 | self.error_code = code 23 | self.error_message = message 24 | 25 | def parse_request(request_text): 26 | request = HTTPRequest(request_text) 27 | 28 | if request.error_code: 29 | raise ParseException(request.error_message) 30 | 31 | return (request.command, request.path, request.request_version, request.request_body, request.headers) 32 | 33 | def usage(): 34 | print "usage: %s target path_to_request real_time? cpu_id delay reps" % (sys.argv[0]) 35 | print "eg: %s https://www.google.com:43562 ./request.txt 1 0 10 100" % (sys.argv[0]) 36 | sys.exit(1) 37 | 38 | 39 | def main(): 40 | if len(sys.argv) != 7: 41 | usage() 42 | 43 | target = sys.argv[1] 44 | path_to_request = sys.argv[2] 45 | real_time = sys.argv[3] 46 | cpu_id = sys.argv[4] 47 | delay = sys.argv[5] 48 | reps = sys.argv[6] 49 | 50 | try: 51 | verb, path, version, body, headers = parse_request(open(path_to_request).read()) 52 | 53 | except ParseException as e: 54 | print "unable to parse request: %s" % e 55 | 56 | args = [CPP_HTTP_TIMING_EXECUTABLE, target + path, verb, version, body, real_time, cpu_id, delay, reps] 57 | args.extend(headers) 58 | print "running %s, args %s" % (CPP_HTTP_TIMING_EXECUTABLE, args) 59 | os.execvp(CPP_HTTP_TIMING_EXECUTABLE, args) 60 | 61 | 62 | if __name__ == '__main__': 63 | main() 64 | -------------------------------------------------------------------------------- /racer/src/clients/SConscript: -------------------------------------------------------------------------------- 1 | Import('env') 2 | 3 | env.Library('#lib/libclient', 4 | Split(""" timing_client.cpp http_timing_client.cpp""")) 5 | -------------------------------------------------------------------------------- /racer/src/clients/http_timing_client.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * http_timing_client.cpp 3 | * 4 | * Created on: May 17, 2014 5 | * Author: jsandin 6 | */ 7 | 8 | #include "http_timing_client.h" 9 | 10 | HTTPTimingClient::HTTPTimingClient(std::string &url, 11 | std::string &verb, 12 | std::string &http_version, 13 | std::string &payload, 14 | vector &headers) { 15 | 16 | this->request_ = make_request_for_version(http_version, url, verb, payload, headers); 17 | } 18 | 19 | vector & HTTPTimingClient::run(long reps) { 20 | vector *ret = new vector(); 21 | TimeHelper t; 22 | 23 | // execute once to open connection and cache dns response 24 | this->request_->execute(); 25 | 26 | for(unsigned int i = 0; i < reps; i++ ) { 27 | t.start(); 28 | t.mark(); 29 | this->request_->execute(); 30 | t.mark(); 31 | vector tmp = t.getDiffVec(); 32 | ret->push_back(tmp[1]); 33 | usleep(1000); 34 | } 35 | return *ret; 36 | 37 | } 38 | 39 | HTTPTimingClient::~HTTPTimingClient() { 40 | } 41 | -------------------------------------------------------------------------------- /racer/src/clients/timing_client.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * TimingClient.cpp 3 | * 4 | * Created on: Mar 23, 2014 5 | * Author: mayer 6 | */ 7 | 8 | #include "timing_client.h" 9 | 10 | 11 | 12 | 13 | TimingClient::TimingClient(string server, int port) { 14 | cout << "Connecting to server..." << endl; 15 | socket = new BasicSizeHeaderSocket; 16 | socket->setupClient(server, port); 17 | } 18 | 19 | vector & TimingClient::run(int delay, int reps) { 20 | vector *ret = new vector(); 21 | int received_val; 22 | TimeHelper t; 23 | for(unsigned int i = 0; i < reps; i++ ) { 24 | t.start(); 25 | t.mark(); 26 | socket->sendInteger(delay); 27 | socket->receiveInteger(received_val); 28 | t.mark(); 29 | vector tmp = t.getDiffVec(); 30 | ret->push_back(tmp[1]); 31 | usleep(1000); 32 | } 33 | return *ret; 34 | 35 | } 36 | 37 | TimingClient::~TimingClient() { 38 | if(socket != NULL) 39 | delete socket; 40 | } 41 | 42 | -------------------------------------------------------------------------------- /racer/src/clients/x_runtime_client.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * http_timing_client.cpp 3 | * 4 | * Created on: May 17, 2014 5 | * Author: jsandin 6 | */ 7 | 8 | #include "x_runtime_client.h" 9 | 10 | XRuntimeClient::XRuntimeClient(std::string &url, 11 | std::string &verb, 12 | std::string &http_version, 13 | std::string &payload, 14 | vector &headers) { 15 | 16 | this->request_ = make_request_for_version(http_version, url, verb, payload, headers); 17 | } 18 | 19 | vector & XRuntimeClient::run(long reps) { 20 | vector *ret = new vector(); 21 | std::string x_runtime_header = "X-Runtime"; 22 | std::string header_value; 23 | 24 | // execute once to open connection and cache dns response 25 | this->request_->execute(); 26 | 27 | for(unsigned int i = 0; i < reps; i++ ) { 28 | this->request_->execute(); 29 | if(this->request_->get_response_code() != 500 && 30 | this->request_->get_response_header(x_runtime_header, header_value)) { 31 | ret->push_back(header_value); 32 | } 33 | else { 34 | cerr << this->request_->get_response_body() << endl; 35 | } 36 | usleep(100); 37 | } 38 | return *ret; 39 | } 40 | 41 | XRuntimeClient::~XRuntimeClient() { 42 | } 43 | -------------------------------------------------------------------------------- /racer/src/servers/SConscript: -------------------------------------------------------------------------------- 1 | Import('env') 2 | 3 | env.Library('#lib/libserver', 4 | Split("""echo_server.cpp nop_echo_server.cpp""")) 5 | -------------------------------------------------------------------------------- /racer/src/servers/echo_server.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * EchoServer.cpp 3 | * 4 | * Created on: Mar 23, 2014 5 | * Author: mayer 6 | */ 7 | 8 | #include "echo_server.h" 9 | #include 10 | 11 | 12 | EchoServer::EchoServer(int port) { 13 | socket = new BasicSizeHeaderSocket(); 14 | socket->setupServer(port); 15 | } 16 | 17 | void EchoServer::loop() { 18 | int wait_time; 19 | int result; 20 | timespec to_wait; 21 | to_wait.tv_sec = 0; 22 | timespec remaining; 23 | while(true) { 24 | socket->receiveInteger(wait_time); 25 | to_wait.tv_nsec = wait_time; 26 | // usleep(wait_time); 27 | result = nanosleep(&to_wait, &remaining); 28 | 29 | socket->sendInteger(wait_time); 30 | if(result != 0) { 31 | std::cout << "Return was not 0." << std::endl; 32 | } 33 | } 34 | 35 | 36 | } 37 | 38 | EchoServer::~EchoServer() { 39 | if(socket != NULL) 40 | delete socket; 41 | 42 | } 43 | 44 | -------------------------------------------------------------------------------- /racer/src/servers/nop_echo_server.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * EchoServer.cpp 3 | * 4 | * Created on: Mar 23, 2014 5 | * Author: mayer 6 | */ 7 | 8 | #include "nop_echo_server.h" 9 | #include 10 | 11 | 12 | NopEchoServer::NopEchoServer(int port, double frequency_in_ghz) { 13 | socket = new BasicSizeHeaderSocket(); 14 | socket->setupServer(port); 15 | this->frequency_in_ghz = frequency_in_ghz; 16 | } 17 | 18 | 19 | // from: https://stackoverflow.com/questions/7935518/is-clock-gettime-adequate-for-submicrosecond-timing 20 | __inline__ uint64_t rdtsc(void) { 21 | uint32_t lo, hi; 22 | __asm__ __volatile__ ( // serialize 23 | "xorl %%eax,%%eax \n cpuid" 24 | ::: "%rax", "%rbx", "%rcx", "%rdx"); 25 | /* We cannot use "=A", since this would use %rax on x86_64 and return only the lower 32bits of the TSC */ 26 | __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); 27 | return (uint64_t)hi << 32 | lo; 28 | } 29 | 30 | 31 | /* 32 | * Assume interrupts are turned off. 33 | * Length of each clock cycle = (1 / ProcessorFrequency) 34 | * Source: http://austinmarton.wordpress.com/2011/02/24/sub-microsecond-delays-in-linux-device-drivers/ 35 | */ 36 | __inline__ void nop_sleep(unsigned long clk_cycs) { 37 | while (clk_cycs-- > 0) 38 | __asm__ volatile ("nop;"); 39 | } 40 | 41 | __inline__ void NopEchoServer::sleep_for(unsigned long nanoseconds) { 42 | uint64_t end = rdtsc() + nanoseconds * this->frequency_in_ghz; 43 | while(rdtsc() < end) { 44 | __asm__ volatile ("nop;"); 45 | } 46 | } 47 | 48 | void NopEchoServer::loop() { 49 | int wait_time; 50 | int result; 51 | long int to_wait; 52 | 53 | while(true) { 54 | socket->receiveInteger(wait_time); 55 | sleep_for(wait_time); 56 | socket->sendInteger(wait_time); 57 | if(result != 0) { 58 | std::cout << "Return was not 0." << std::endl; 59 | } 60 | } 61 | 62 | 63 | } 64 | 65 | NopEchoServer::~NopEchoServer() { 66 | if(socket != NULL) 67 | delete socket; 68 | 69 | } 70 | 71 | -------------------------------------------------------------------------------- /racer/src/socket/SConscript: -------------------------------------------------------------------------------- 1 | Import('env') 2 | 3 | env.Library('#lib/libsocket', 4 | Split("""raw_socket.cpp 5 | basic_size_header_socket.cpp""")) 6 | -------------------------------------------------------------------------------- /racer/src/socket/basic_size_header_socket.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BasicSizeHeaderSocket.cpp 3 | * 4 | * Created on: Oct 14, 2009 5 | * Author: mayer 6 | */ 7 | 8 | #include "basic_size_header_socket.h" 9 | #define MPZEXPORTWORDSIZE 4 10 | 11 | #include 12 | 13 | 14 | BasicSizeHeaderSocket::BasicSizeHeaderSocket() { 15 | 16 | } 17 | 18 | BasicSizeHeaderSocket::~BasicSizeHeaderSocket() { 19 | } 20 | 21 | void BasicSizeHeaderSocket::send(const vector& data) 22 | { 23 | int i; 24 | 25 | sendInteger(data.size()); 26 | for(i = 0; i < data.size(); i++) 27 | send(data[i]); 28 | } 29 | 30 | void BasicSizeHeaderSocket::send(const set& data) 31 | { 32 | set::iterator it; 33 | 34 | sendInteger(data.size()); 35 | for(it = data.begin();it != data.end(); it++) 36 | send(*it); 37 | } 38 | 39 | void BasicSizeHeaderSocket::send(const mpz_class& data) { 40 | unsigned long int words; 41 | void * toSend = mpz_export(NULL, (size_t*) &words, -1, MPZEXPORTWORDSIZE, -1, 0, data.get_mpz_t()); 42 | this->RawSocket::sendInteger(words); 43 | this->RawSocket::sendData(toSend, words * MPZEXPORTWORDSIZE); 44 | free(toSend); 45 | } 46 | 47 | void BasicSizeHeaderSocket::send(const string& data) { 48 | this->RawSocket::sendInteger((int) (data.length() * sizeof(char))); 49 | this->RawSocket::sendChars(data.c_str()); 50 | } 51 | 52 | void BasicSizeHeaderSocket::sendInteger(const int data) { 53 | RawSocket::sendInteger(data); 54 | } 55 | 56 | vector& BasicSizeHeaderSocket::receive(vector& data) 57 | { 58 | int num; 59 | mpz_class tmp; 60 | 61 | num = receiveInteger(num); 62 | 63 | for(;num > 0; num--) 64 | data.push_back(receive(tmp)); 65 | 66 | return data; 67 | } 68 | 69 | set& BasicSizeHeaderSocket::receive(set& data) 70 | { 71 | int num; 72 | mpz_class tmp; 73 | 74 | num = receiveInteger(num); 75 | 76 | for(;num > 0; num--) 77 | data.insert(receive(tmp)); 78 | 79 | return data; 80 | } 81 | 82 | mpz_class& BasicSizeHeaderSocket::receive(mpz_class& data) { 83 | int words; 84 | this->RawSocket::receiveInteger(words); 85 | char buf[words * MPZEXPORTWORDSIZE]; 86 | 87 | this->RawSocket::receiveData(&buf,words * MPZEXPORTWORDSIZE); 88 | mpz_import(data.get_mpz_t(), words, -1, MPZEXPORTWORDSIZE, -1, 0, (void*) &buf); 89 | 90 | return data; 91 | } 92 | 93 | 94 | string& BasicSizeHeaderSocket::receive(string& data) { 95 | int len; 96 | string tmp; 97 | this->RawSocket::receiveInteger(len); 98 | this->RawSocket::receiveString(data,len); 99 | 100 | return data; 101 | } 102 | 103 | int& BasicSizeHeaderSocket::receiveInteger(int& rop) { 104 | return RawSocket::receiveInteger(rop); 105 | } 106 | -------------------------------------------------------------------------------- /racer/src/socket/raw_socket.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * RawSockets.cpp 3 | * 4 | * Created on: Aug 31, 2009 5 | * Author: mayer 6 | */ 7 | 8 | 9 | #include "raw_socket.h" 10 | 11 | RawSocket::RawSocket() { 12 | // init to -1, to identify a server and client socket 13 | serverSocketFd = -1; 14 | } 15 | 16 | RawSocket::~RawSocket() { 17 | // TODO free the buffers 18 | } 19 | 20 | 21 | void RawSocket::setupServer(unsigned int port) { 22 | 23 | socklen_t clilen; 24 | 25 | // structs for server and client addresses 26 | struct sockaddr_in serverAddress, clientAddress; 27 | 28 | 29 | 30 | 31 | // create server socket 32 | // AF_INET: use internet domain sockets 33 | // SOCK_STREAM: stream socket, compare tp datagram socket 34 | // 0: protocol, OS will choose automatically 35 | serverSocketFd = socket(AF_INET, SOCK_STREAM, 0); 36 | if (serverSocketFd < 0) { 37 | perror("Socket could not be created"); 38 | 39 | //throw socketOpenException; 40 | exit(1); 41 | } 42 | 43 | 44 | // when the socket is closed it might be in a waiting state before it 45 | // closes (TIME_WAIT). SO_REUSEADDR allows re-use of such a socket. 46 | int on = 1; 47 | if(setsockopt(serverSocketFd,SOL_SOCKET, SO_REUSEADDR,&on,sizeof(on)) == -1) { 48 | perror("setsockopt"); 49 | return exit(1); 50 | } 51 | 52 | // zero serverAddress buffer 53 | bzero((char *) &serverAddress, sizeof(serverAddress)); 54 | 55 | 56 | // setup server struct 57 | serverAddress.sin_family = AF_INET; // address family 58 | serverAddress.sin_addr.s_addr = INADDR_ANY; // listen on all local addresses 59 | serverAddress.sin_port = htons(port); // convert port to network byte order 60 | 61 | 62 | // bind our socket to the address 63 | if (bind(serverSocketFd, (struct sockaddr *) &serverAddress, 64 | sizeof(serverAddress)) < 0) { 65 | perror("Could not bind"); 66 | exit(1); 67 | } 68 | 69 | // listen on the socket for incoming connections 70 | listen(serverSocketFd, 5); // backlog of 5 71 | 72 | 73 | // wait for incoming conenction 74 | clilen = sizeof(clientAddress); 75 | socketFd = accept(serverSocketFd, (struct sockaddr *) &clientAddress, &clilen); 76 | 77 | if (socketFd < 0){ 78 | exit(1); 79 | } 80 | 81 | //finally, disable Nagle's algorithm on the newly established socket 82 | //this usually improves efficiency but causes timing problems 83 | int flag = 1; 84 | int result = setsockopt(socketFd, // socket affected 85 | IPPROTO_TCP, // set option at TCP level 86 | TCP_NODELAY, // name of option 87 | (char *) &flag, // the cast is historical cruft 88 | sizeof(int)); // length of option value 89 | 90 | if(result < 0) { 91 | exit(1); 92 | } 93 | 94 | std::cout << "Server listening.." << std::endl; 95 | } 96 | 97 | 98 | void RawSocket::setupClient(string address, unsigned int port) { 99 | struct sockaddr_in serverAddress; 100 | struct hostent *server; 101 | 102 | // create client socket 103 | // AF_INET: use internet domain sockets 104 | // SOCK_STREAM: stream socket, compare tp datagram socket 105 | // 0: protocol, OS will choose automatically 106 | socketFd = socket(AF_INET, SOCK_STREAM, 0); 107 | if (socketFd < 0) { 108 | exit(1); 109 | } 110 | 111 | // resolve servername 112 | server = gethostbyname(address.c_str()); 113 | if (server == NULL) { 114 | fprintf(stderr,"ERROR, no such host\n"); 115 | exit(1); 116 | } 117 | 118 | // zero serverAddress buffer 119 | bzero((char *) &serverAddress, sizeof(serverAddress)); 120 | serverAddress.sin_family = AF_INET; 121 | bcopy((char *)server->h_addr, (char *)&serverAddress.sin_addr.s_addr, server->h_length); 122 | 123 | 124 | // set port and attempt to connect 125 | serverAddress.sin_port = htons(port); 126 | while(connect(socketFd, (sockaddr*) &serverAddress, sizeof(serverAddress)) < 0) { 127 | sleep(1); 128 | } 129 | 130 | //finally, disable Nagle's algorithm on the newly established socket 131 | //this usually improves efficiency but causes timing problems 132 | int flag = 1; 133 | int result = setsockopt(socketFd, // socket affected 134 | IPPROTO_TCP, // set option at TCP level 135 | TCP_NODELAY, // name of option 136 | (char *) &flag, // the cast is historical cruft 137 | sizeof(int)); // length of option value 138 | 139 | if(result < 0) { 140 | exit(1); 141 | } 142 | 143 | printf("%s\n","Connection established"); 144 | 145 | } 146 | 147 | 148 | void RawSocket::closeSocket(){ 149 | shutdown(socketFd,SHUT_RDWR); 150 | close(socketFd); 151 | perror("sockFd"); 152 | 153 | if(serverSocketFd != -1){ 154 | shutdown(serverSocketFd,SHUT_RDWR); 155 | close(serverSocketFd); 156 | perror("serverSocketFd"); 157 | } 158 | } 159 | 160 | 161 | /** 162 | * Basic send function. Writes a string "data" to the socket 163 | */ 164 | void RawSocket::sendString(const string& data) { 165 | int n = write(socketFd, data.c_str(), (int) data.length()); 166 | if(n < 0) { 167 | perror("Error writing to socket"); 168 | //TODO throw an exception 169 | } 170 | } 171 | 172 | void RawSocket::sendChars(const char* data) { 173 | int n = write(socketFd, data, strlen(data) * sizeof(char)); 174 | if(n < 0) { 175 | perror("Error writing to socket"); 176 | //TODO throw an exception 177 | } 178 | } 179 | 180 | void RawSocket::sendData(const void* data, unsigned long bytes) { 181 | int n = write(socketFd, data, bytes); 182 | if(n < 0) { 183 | perror("Error writing to socket"); 184 | //TODO throw an exception 185 | } 186 | } 187 | 188 | /** 189 | * Basic send function. Writes a long "data" to the socket. 190 | * The function converts the data first to network byte order. 191 | */ 192 | void RawSocket::sendLong(const long data) { 193 | // uint64_t toSend = htonl(data); 194 | // convert integer to network byte order and transmit 195 | int n = write(socketFd, (char*) &data, sizeof(data)); 196 | if(n < 0) { 197 | perror("Error writing to socket"); 198 | //TODO throw an exception 199 | } 200 | } 201 | 202 | 203 | 204 | /** 205 | * Basic send function. Writes a int "data" to the socket. 206 | * The function converts the data first to network byte order. 207 | */ 208 | void RawSocket::sendInteger(const int data) { 209 | uint32_t toSend = htonl(data); 210 | // convert integer to network byte order and transmit 211 | int n = write(socketFd, (char*) &toSend, sizeof(toSend)); 212 | if(n < 0) { 213 | perror("Error writing to socket"); 214 | //TODO throw an exception 215 | } 216 | } 217 | 218 | /** 219 | * Basic receive function. Reads "bytes" bytes from the socket and 220 | * returns them in "data" 221 | */ 222 | string& RawSocket::receiveString(string& data, unsigned long bytes) { 223 | char buf[bytes+1]; 224 | 225 | int n = read(socketFd, &buf, bytes); 226 | if (n < 0) { 227 | perror("ERROR reading from socket"); 228 | //TODO throw an exception 229 | } 230 | // add null-terminator 231 | buf[bytes] = '\0'; 232 | data.append(buf); 233 | 234 | return data; 235 | } 236 | 237 | /** 238 | * recevies an integer as byte array over the network 239 | * and converts it into host byte order 240 | */ 241 | int& RawSocket::receiveInteger(int& data) { 242 | int tmp; 243 | 244 | int n = read(socketFd, &tmp, sizeof(uint32_t)); 245 | if (n < 0) { 246 | perror("ERROR reading from socket"); 247 | //TODO throw an exception 248 | } 249 | data = ntohl(tmp); 250 | return data; 251 | } 252 | 253 | 254 | /** 255 | * recevies an integer as byte array over the network 256 | * and converts it into host byte order 257 | */ 258 | long& RawSocket::receiveLong(long& data) { 259 | // long tmp; 260 | 261 | //TOOD: Skipping endianness conversion. This assumes both hosts use the same Endianness. 262 | int n = read(socketFd, &data, sizeof(uint64_t)); 263 | if (n < 0) { 264 | perror("ERROR reading from socket"); 265 | //TODO throw an exception 266 | } 267 | // data = ntohl(tmp); 268 | return data; 269 | } 270 | 271 | 272 | 273 | 274 | /** 275 | * Basic receive function. Reads "bytes" bytes from the socket and 276 | * returns them in "data" 277 | */ 278 | void* RawSocket::receiveData(void* data, unsigned long bytes) { 279 | int n = read(socketFd, data, bytes); 280 | if (n < 0) { 281 | perror("ERROR reading from socket"); 282 | //TODO throw an exception 283 | } 284 | 285 | return data; 286 | } 287 | 288 | 289 | 290 | 291 | 292 | 293 | -------------------------------------------------------------------------------- /racer/src/utils/SConscript: -------------------------------------------------------------------------------- 1 | Import('env') 2 | 3 | env.Library('#lib/libutils', 4 | Split("""time_helper.cpp 5 | http_request.cpp 6 | string_split.cpp""")) 7 | -------------------------------------------------------------------------------- /racer/src/utils/http_request.cpp: -------------------------------------------------------------------------------- 1 | #include "http_request.h" 2 | #include "time_helper.h" 3 | #include "string_split.h" 4 | 5 | using namespace boost::network; 6 | using namespace boost::network::http; 7 | 8 | Http11Request::Http11Request(std::string &url, 9 | std::string &verb, 10 | std::string &payload, 11 | std::vector &headers) { 12 | 13 | http11_client::options options; 14 | options.follow_redirects(false) 15 | .cache_resolved(true); 16 | 17 | client_ = http11_client(options); 18 | request_ = http11_client::request(url); 19 | 20 | if(verb == "GET") { 21 | verb_ = &Http11Request::get; 22 | } 23 | else if(verb == "HEAD") { 24 | verb_ = &Http11Request::head; 25 | } 26 | else if(verb == "POST") { 27 | verb_ = &Http11Request::post; 28 | } 29 | else if(verb == "PUT") { 30 | verb_ = &Http11Request::put; 31 | } 32 | else if(verb == "DELETE") { 33 | verb_ = &Http11Request::delete_; 34 | } 35 | else { 36 | throw InvalidHttpVerb(); 37 | } 38 | 39 | for(std::vector::iterator it = headers.begin(); it != headers.end(); ++it) { 40 | std::vector header_values = split(*it, ':', 1); 41 | if(header_values.size() != 2) { 42 | throw InvalidHttpHeader(); 43 | } 44 | request_ << boost::network::header(header_values[0], header_values[1]); 45 | } 46 | 47 | if(payload != "") { 48 | request_ << boost::network::body(payload); 49 | } 50 | } 51 | 52 | 53 | Http11Request::~Http11Request() {} 54 | 55 | void Http11Request::get() { response_ = client_.get(request_); } 56 | void Http11Request::head() { response_ = client_.head(request_); } 57 | void Http11Request::post() { response_ = client_.post(request_); } 58 | void Http11Request::put() { response_ = client_.put(request_); } 59 | void Http11Request::delete_() { response_ = client_.delete_(request_); } 60 | 61 | uint16_t Http11Request::get_response_code() { return status(response_); } 62 | std::string Http11Request::get_response_body() { return body(response_); } 63 | 64 | void Http11Request::execute() { 65 | // function pointer to our verb: We could also do this with a 66 | // single virtual function and Http11GetRequest, Http11PostRequest etc 67 | (this->*verb_)(); 68 | } 69 | 70 | Http10Request::Http10Request(std::string &url, 71 | std::string &verb, 72 | std::string &payload, 73 | std::vector &headers) { 74 | 75 | http10_client::options options; 76 | options.follow_redirects(false) 77 | .cache_resolved(true); 78 | 79 | client_ = http10_client(options); 80 | request_ = http10_client::request(url); 81 | 82 | if(verb == "GET") { 83 | verb_ = &Http10Request::get; 84 | } 85 | else if(verb == "HEAD") { 86 | verb_ = &Http10Request::head; 87 | } 88 | else if(verb == "POST") { 89 | verb_ = &Http10Request::post; 90 | } 91 | else if(verb == "PUT") { 92 | verb_ = &Http10Request::put; 93 | } 94 | else if(verb == "DELETE") { 95 | verb_ = &Http10Request::delete_; 96 | } 97 | else { 98 | throw InvalidHttpVerb(); 99 | } 100 | 101 | for(std::vector::iterator it = headers.begin(); it != headers.end(); ++it) { 102 | std::vector header_values = split(*it, ':', 1); 103 | if(header_values.size() != 2) { 104 | throw InvalidHttpHeader(); 105 | } 106 | request_ << boost::network::header(header_values[0], header_values[1]); 107 | } 108 | 109 | if(payload != "") { 110 | request_ << boost::network::body(payload); 111 | } 112 | } 113 | 114 | Http10Request::~Http10Request() {} 115 | 116 | void Http10Request::get() { response_ = client_.get(request_); } 117 | void Http10Request::head() { response_ = client_.head(request_); } 118 | void Http10Request::post() { response_ = client_.post(request_); } 119 | void Http10Request::put() { response_ = client_.put(request_); } 120 | void Http10Request::delete_() { response_ = client_.delete_(request_); } 121 | 122 | uint16_t Http10Request::get_response_code() { return status(response_); } 123 | std::string Http10Request::get_response_body() { return body(response_); } 124 | 125 | void Http10Request::execute() { 126 | // function pointer to our verb: We could also do this with a 127 | // single virtual function and Http11GetRequest, Http11PostRequest etc 128 | (this->*verb_)(); 129 | } 130 | 131 | HttpRequest *make_request_for_version(std::string http_version, 132 | std::string url, 133 | std::string verb, 134 | std::string payload, 135 | std::vector headers) { 136 | if(http_version == "HTTP/1.1") { 137 | return new Http11Request(url, verb, payload, headers); 138 | } 139 | else if(http_version == "HTTP/1.0") { 140 | return new Http10Request(url, verb, payload, headers); 141 | } 142 | throw InvalidHttpVersion(); 143 | } 144 | -------------------------------------------------------------------------------- /racer/src/utils/string_split.cpp: -------------------------------------------------------------------------------- 1 | #include "string_split.h" 2 | 3 | // don't use this on long strings! 4 | std::vector &split(const std::string &s, char delim, std::vector &elems, unsigned int count) { 5 | std::stringstream ss(s); 6 | std::string item; 7 | std::string lastitem; 8 | 9 | while (count > 0 && std::getline(ss, item, delim)) { 10 | elems.push_back(item); 11 | count--; 12 | } 13 | while (!ss.eof()) { 14 | std::getline(ss, item); 15 | // slow for many tokens 16 | lastitem += item; 17 | } 18 | elems.push_back(lastitem); 19 | return elems; 20 | } 21 | 22 | std::vector split(const std::string &s, char delim, unsigned int count) { 23 | std::vector elems; 24 | elems = split(s, delim, elems, count); 25 | return elems; 26 | } 27 | -------------------------------------------------------------------------------- /racer/src/utils/strings.cpp: -------------------------------------------------------------------------------- 1 | #include "strings.h" 2 | 3 | // don't use this on long strings! 4 | std::vector &split(const std::string &s, char delim, std::vector &elems, unsigned int count) { 5 | std::stringstream ss(s); 6 | std::string item; 7 | std::string lastitem; 8 | 9 | while (count > 0 && std::getline(ss, item, delim)) { 10 | elems.push_back(item); 11 | count--; 12 | } 13 | while (!ss.eof()) { 14 | std::getline(ss, item); 15 | // slow for many tokens 16 | lastitem += item; 17 | } 18 | elems.push_back(lastitem); 19 | return elems; 20 | } 21 | 22 | std::vector split(const std::string &s, char delim, unsigned int count) { 23 | std::vector elems; 24 | elems = split(s, delim, elems, count); 25 | return elems; 26 | } 27 | 28 | bool get_first_capture(const boost::regex &ex, const std::string st, std::string &capture) { 29 | std::string::const_iterator start, end; 30 | start = st.begin(); 31 | end = st.end(); 32 | boost::match_results what; 33 | boost::match_flag_type flags = boost::match_default; 34 | if(boost::regex_search(start, end, what, ex, flags)) { 35 | capture = what[1]; 36 | return true; 37 | } 38 | return false; 39 | } 40 | -------------------------------------------------------------------------------- /racer/src/utils/time_helper.cpp: -------------------------------------------------------------------------------- 1 | #include "time_helper.h" 2 | 3 | /* Constructor: ignores tag functionality if no string is given */ 4 | TimeHelper::TimeHelper() 5 | { 6 | tag = "N/A"; 7 | active = 0; 8 | } 9 | 10 | /* Constructor: takes a string to identify timer by */ 11 | TimeHelper::TimeHelper(string s) 12 | { 13 | tag = s; 14 | active = 0; 15 | } 16 | 17 | void TimeHelper::current_utc_time(timespec * ts) { 18 | #ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time 19 | clock_serv_t cclock; 20 | mach_timespec_t mts; 21 | host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); 22 | clock_get_time(cclock, &mts); 23 | mach_port_deallocate(mach_task_self(), cclock); 24 | ts->tv_sec = mts.tv_sec; 25 | ts->tv_nsec = mts.tv_nsec; 26 | #else 27 | clock_gettime(CLOCK_MONOTONIC, ts); 28 | #endif 29 | 30 | } 31 | 32 | /* Starts or Re-Starts the timer. (Restarting will ignore previous marks) */ 33 | void TimeHelper::start() 34 | { 35 | marks.clear(); 36 | // start_wall = times(&start_times); 37 | current_utc_time(&start_clock_time); // Works on Linux 38 | 39 | active = 1; 40 | } 41 | 42 | /* Marks the timer's current time since starting. Does nothing if never started */ 43 | void TimeHelper::mark() 44 | { 45 | if(active) 46 | { 47 | TimeMark tmp; 48 | // mark_wall = times(&mark_times); 49 | // active = 2; 50 | // tmp.utime = do_utime(); 51 | // tmp.stime = do_stime(); 52 | // tmp.wtime = do_wtime(); 53 | // tmp.ticks = do_ticks(); 54 | current_utc_time(&tmp.clock_time); // Works on Linux 55 | marks.push_back(tmp); 56 | } else { 57 | cout << "not active" << endl; 58 | } 59 | } 60 | 61 | void TimeHelper::mark(string name){ 62 | this->mark(); 63 | marks.back().name = name; 64 | } 65 | 66 | 67 | /* Get functions for most recent ellapsed times recorded with mark(). returns -1 if problem */ 68 | unsigned long TimeHelper::utime() 69 | { 70 | if(marks.empty()) 71 | return -1; 72 | return marks.back().utime; 73 | } 74 | 75 | unsigned long TimeHelper::stime() 76 | { 77 | if(marks.empty()) 78 | return -1; 79 | return marks.back().stime; 80 | } 81 | 82 | unsigned long TimeHelper::wtime() 83 | { 84 | if(marks.empty()) 85 | return -1; 86 | return marks.back().wtime; 87 | } 88 | 89 | /* Get functions to return ellapsed times at an index. returns -1 if problem exists */ 90 | unsigned long TimeHelper::utime(int j) 91 | { 92 | if((j >= marks.size()) && (j >= 0)) 93 | return -1; 94 | return marks[j].utime; 95 | } 96 | 97 | unsigned long TimeHelper::stime(int j) 98 | { 99 | if((j >= marks.size()) && (j >= 0)) 100 | return -1; 101 | return marks[j].stime; 102 | } 103 | 104 | unsigned long TimeHelper::wtime(int j) 105 | { 106 | if((j >= marks.size()) && (j >= 0)) 107 | return -1; 108 | return marks[j].wtime; 109 | } 110 | 111 | /* Helper functions to do time calculation when needed */ 112 | unsigned long TimeHelper::do_ticks() 113 | { 114 | if(active != 2) 115 | return -1; 116 | return (mark_ticks - start_ticks); 117 | } 118 | 119 | unsigned long TimeHelper::do_utime() 120 | { 121 | if(active != 2) 122 | return -1; 123 | return (mark_times.tms_utime - start_times.tms_utime) * 1000 / sysconf(_SC_CLK_TCK); 124 | } 125 | 126 | unsigned long TimeHelper::do_stime() 127 | { 128 | if(active != 2) 129 | return -1; 130 | return (mark_times.tms_stime - start_times.tms_stime) * 1000 / sysconf(_SC_CLK_TCK); 131 | } 132 | 133 | unsigned long TimeHelper::do_wtime() 134 | { 135 | if(active != 2) 136 | return -1; 137 | return (mark_wall - start_wall) * 1000 / sysconf(_SC_CLK_TCK); 138 | } 139 | 140 | /* String return function to print all marked timer data. Will print error if unmarked or unstarted */ 141 | string TimeHelper::str() 142 | { 143 | stringstream ss; 144 | int i; 145 | 146 | if(marks.empty()) 147 | return "Error: Inactive or unmarked time\n"; 148 | ss << "Tag: \t " << tag << endl; 149 | for(i = 0; i < marks.size(); i++) 150 | { 151 | ss << "utime: " << marks[i].utime << " stime: " << marks[i].stime << " wall: " << marks[i].wtime << " mark#: " << i << endl; 152 | } 153 | return ss.str(); 154 | } 155 | 156 | /* String return function to print marked timer data at a specific index. Will print error if unmarked or unstarted */ 157 | string TimeHelper::str(int j) 158 | { 159 | stringstream ss; 160 | 161 | if((j >= marks.size()) && (j >= 0)) 162 | return "Error: Inactive or unmarked time\n"; 163 | ss << "utime: " << marks[j].utime << " stime: " << marks[j].stime << " wall: " << marks[j].wtime << " tag: " << tag << " mark#: " << j; 164 | return ss.str(); 165 | } 166 | 167 | vector TimeHelper::getVec() 168 | { 169 | return marks; 170 | } 171 | 172 | timespec diff_timespec(timespec start, timespec end) 173 | { 174 | timespec temp; 175 | if ((end.tv_nsec-start.tv_nsec)<0) { 176 | temp.tv_sec = end.tv_sec-start.tv_sec-1; 177 | temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; 178 | } else { 179 | temp.tv_sec = end.tv_sec-start.tv_sec; 180 | temp.tv_nsec = end.tv_nsec-start.tv_nsec; 181 | } 182 | return temp; 183 | } 184 | 185 | /** 186 | * Returns the time elapsed between each mark 187 | */ 188 | vector TimeHelper::getDiffVec(){ 189 | vector split_times = marks; 190 | vector diff_times; 191 | for(unsigned int i = 0; i < split_times.size(); i++) { 192 | // subtract the time for the previous mark, to get interval times. 193 | TimeMark newMark; 194 | if( i > 0 ) { 195 | // newMark.utime = (split_times[i].utime - split_times[i-1].utime); 196 | // newMark.stime = (split_times[i].stime - split_times[i-1].stime); 197 | // newMark.wtime = (split_times[i].wtime - split_times[i-1].wtime); 198 | // newMark.ticks = (split_times[i].ticks - split_times[i-1].ticks); 199 | 200 | 201 | newMark.clock_time = diff_timespec(split_times[i-1].clock_time, split_times[i].clock_time); 202 | 203 | // except for the first time 204 | } else { 205 | // newMark.utime = split_times[i].utime; 206 | // newMark.stime = split_times[i].stime; 207 | // newMark.wtime = split_times[i].wtime; 208 | // newMark.ticks = split_times[i].ticks; 209 | newMark.clock_time = split_times[i].clock_time; 210 | } 211 | diff_times.push_back(newMark); 212 | } 213 | return diff_times; 214 | } 215 | 216 | 217 | 218 | 219 | TimeMark TimeHelper::getMark() 220 | { 221 | TimeMark tmp; 222 | if(marks.empty()) 223 | return tmp; 224 | return *marks.end(); 225 | } 226 | 227 | TimeMark TimeHelper::getMark(int j) 228 | { 229 | TimeMark tmp; 230 | if((j >= marks.size()) && (j >= 0)) 231 | return tmp; 232 | return marks[j]; 233 | } 234 | 235 | 236 | -------------------------------------------------------------------------------- /time_trial_gui/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmayer/time_trial/c9f896f8788bb4ae3b8bd526a325542c610d0573/time_trial_gui/.DS_Store -------------------------------------------------------------------------------- /time_trial_gui/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /time_trial_gui/gui/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'daniel' 2 | -------------------------------------------------------------------------------- /time_trial_gui/gui/data_source_model.py: -------------------------------------------------------------------------------- 1 | from PyQt4 import QtGui, QtCore, Qt 2 | 3 | class DataSourceModel(QtCore.QAbstractTableModel): 4 | 5 | def __init__(self, parent = None, *args): 6 | QtCore.QAbstractTableModel.__init__(self, parent, *args) 7 | self.data_store = [] 8 | self.header_vals = ["File Name", "Label", "Color", "Bins", "Style"] 9 | self.default_colors = ["red", "blue", "green", "black"] 10 | 11 | def rowCount(self, parent): 12 | return len(self.data_store) 13 | 14 | def columnCount(self, parent): 15 | return 5 16 | 17 | def headerData(self, p_int, Qt_Orientation, int_role=None): 18 | if int_role == QtCore.Qt.DisplayRole and Qt_Orientation == QtCore.Qt.Horizontal: 19 | return self.header_vals[p_int] 20 | return QtCore.QAbstractTableModel.headerData(self, p_int, Qt_Orientation, int_role) 21 | 22 | 23 | def data(self, index, role): 24 | if not index.isValid(): 25 | return None 26 | if role == QtCore.Qt.EditRole: 27 | item = self.data_store[index.row()] 28 | return item 29 | elif role != QtCore.Qt.DisplayRole: 30 | return None 31 | else: 32 | item = self.data_store[index.row()] 33 | if index.column() == 0: 34 | return item.timing_data.file_name 35 | elif index.column() == 1: 36 | return item.label 37 | elif index.column() == 2: 38 | return item.color 39 | elif index.column() == 3: 40 | return item.bins 41 | elif index.column() == 4: 42 | return item.style_name() 43 | 44 | def add_data(self, item): 45 | # index = self.createIndex(len(self.data_store), 0) 46 | self.beginInsertRows(QtCore.QModelIndex(), len(self.data_store), len(self.data_store)) 47 | self.data_store.append(item) 48 | item.color = self.default_colors[len(self.data_store)-1] 49 | # self.dataChanged.emit(index, index) 50 | self.endInsertRows() 51 | -------------------------------------------------------------------------------- /time_trial_gui/gui/experiment_combo_box.py: -------------------------------------------------------------------------------- 1 | from PyQt4 import QtGui 2 | from models.experiment import Experiment 3 | 4 | __author__ = 'daniel' 5 | 6 | class ExperimentComboBox(QtGui.QComboBox): 7 | 8 | def __init__(self, session = None, parent = None): 9 | super(ExperimentComboBox, self).__init__(parent) 10 | self.session = session 11 | self.refresh_experiments() 12 | 13 | def refresh_experiments(self): 14 | self.clear() 15 | self.experiments = self.session.query(Experiment).all() 16 | for e in self.experiments: 17 | self.addItem(e.name) 18 | 19 | def currentItem(self): 20 | try: 21 | val = self.experiments[self.currentIndex()] 22 | except Exception as e: 23 | print(e) 24 | return None 25 | 26 | return val 27 | 28 | -------------------------------------------------------------------------------- /time_trial_gui/gui/experiments_tab.py: -------------------------------------------------------------------------------- 1 | from PyQt4 import QtGui,QtCore 2 | from datetime import datetime 3 | from rq.job import Job 4 | from redis import Redis 5 | from rq import Queue 6 | from gui.experiment_combo_box import ExperimentComboBox 7 | from gui.new_trial_dialog import NewTrialDialog 8 | from gui.sqlalchemy_table_model import SQLAlchemyTableModel 9 | from gui.trial_detail_widget import EchoTrialDetailsWidget, HttpTrialDetailsWidget, RacerDetailsWidget, \ 10 | TrialStatusWidget 11 | from lib.racer_driver import execute_trial 12 | from lib.trial_jobs import EchoTrialJob, HTTPTrialJob 13 | 14 | from models.experiment import Experiment 15 | from models.trial import Trial 16 | 17 | __author__ = 'daniel' 18 | 19 | class ExperimentsTab(QtGui.QWidget): 20 | 21 | 22 | 23 | 24 | def __init__(self, session = None, parent = None): 25 | super(ExperimentsTab, self).__init__(parent) 26 | self.session = session 27 | self.redis_conn = Redis() 28 | 29 | 30 | self.current_experiment = None 31 | 32 | 33 | self.layout = QtGui.QGridLayout() 34 | self.setLayout(self.layout) 35 | 36 | # experiments 37 | self.experiment_box = QtGui.QGroupBox(self, title="Experiments") 38 | self.experiment_box.setSizePolicy(QtGui.QSizePolicy.Minimum,QtGui.QSizePolicy.Minimum) 39 | self.layout.addWidget(self.experiment_box,0,0) 40 | self.experiment_box_layout = QtGui.QGridLayout(self.experiment_box) 41 | self.experiment_box.setLayout(self.experiment_box_layout) 42 | 43 | 44 | self.experiment_list = ExperimentComboBox(session = session) 45 | self.experiment_list.currentIndexChanged.connect(self.update_current_experiment) 46 | self.experiment_box_layout.addWidget(self.experiment_list, 0, 0) 47 | 48 | self.new_experiment_button = QtGui.QPushButton("New Experiment") 49 | self.new_experiment_button.released.connect(self.new_experiment) 50 | self.experiment_box_layout.addWidget(self.new_experiment_button, 0, 1) 51 | 52 | 53 | # data sources 54 | self.data_box = QtGui.QGroupBox(self, title="Trials for this Experiment") 55 | self.data_box.setSizePolicy(QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding ) 56 | self.layout.addWidget(self.data_box,1,0, 1, 1) 57 | 58 | self.data_box_layout = QtGui.QGridLayout(self.data_box) 59 | self.data_box.setLayout(self.data_box_layout) 60 | 61 | 62 | 63 | 64 | 65 | self.trial_table = QtGui.QTableView(self) 66 | # self.trial_table.doubleClicked.connect(self.edit_racer) 67 | # self.trial_table.activated.connect(self.update_current_trial) 68 | self.trial_table.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) 69 | self.trial_table_model = SQLAlchemyTableModel(session, Trial, [ 70 | ('Type', Trial.discriminator, 'discriminator'), 71 | ('Name', Trial.name, 'name'), 72 | ('Reps', Trial.reps, 'reps'), 73 | ('Start', Trial.start_date, 'start_date'), 74 | ('End', Trial.end_date, 'end_date') 75 | ]) 76 | 77 | self.trial_table.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) 78 | self.trial_table.customContextMenuRequested.connect(self.display_context_menu) 79 | self.trial_table.doubleClicked.connect(self.edit_trial) 80 | self.trial_table.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) 81 | 82 | 83 | self.trial_table_selection_model = QtGui.QItemSelectionModel(self.trial_table_model) 84 | self.trial_table.setModel(self.trial_table_selection_model.model()) 85 | self.trial_table.setSelectionModel(self.trial_table_selection_model) 86 | self.trial_table_selection_model.selectionChanged.connect(self.update_current_trial) 87 | 88 | 89 | self.data_box_layout.addWidget(self.trial_table, 0, 0, 2, 1) 90 | 91 | self.add_trial_button = QtGui.QPushButton("New Trial") 92 | self.data_box_layout.addWidget(self.add_trial_button, 0, 1) 93 | self.add_trial_button.released.connect(self.new_trial) 94 | 95 | 96 | self.trial_details_box = QtGui.QGroupBox("Trial Details") 97 | self.trial_details_box.setSizePolicy(QtGui.QSizePolicy.Minimum,QtGui.QSizePolicy.Minimum) 98 | self.data_box_layout.addWidget(self.trial_details_box, 2, 0, 1, 1) 99 | self.trial_details_box_layout = QtGui.QGridLayout(self.trial_details_box) 100 | 101 | self.echo_trial_details = EchoTrialDetailsWidget() 102 | self.trial_details_box_layout.addWidget(self.echo_trial_details, 0, 0, 1, 1) 103 | self.echo_trial_details.hide() 104 | 105 | self.http_trial_details = HttpTrialDetailsWidget() 106 | self.trial_details_box_layout.addWidget(self.http_trial_details, 0, 0, 1, 1) 107 | 108 | self.racer_settings_widget = RacerDetailsWidget() 109 | self.trial_details_box_layout.addWidget(self.racer_settings_widget, 1, 0, 1, 1) 110 | 111 | self.trial_status = TrialStatusWidget() 112 | # self.trial_status.trial_edit.connect(self.show_edit_dialog) 113 | self.trial_status.trial_started.connect(self.start_trial) 114 | self.trial_status.trial_stopped.connect(self.stop_trial) 115 | self.trial_status.trial_refreshed.connect(self.update_trial_details) 116 | self.trial_status.trial_edit.connect(self.edit_trial) 117 | self.trial_details_box_layout.addWidget(self.trial_status, 0, 1, 2, 1) 118 | 119 | self.update_current_experiment(0) 120 | self.trial_table.resizeColumnsToContents() 121 | 122 | 123 | def display_context_menu(self, pos): 124 | index = self.trial_table.indexAt(pos) 125 | 126 | 127 | 128 | self. menu = QtGui.QMenu() 129 | 130 | self.edit_action = self.menu.addAction("Edit") 131 | self.edit_action.triggered.connect(self.edit_trial) 132 | 133 | self.duplicate_action = self.menu.addAction("Duplicate") 134 | self.duplicate_action.triggered.connect(self.duplicate_trial) 135 | 136 | self.delete_action = self.menu.addAction("Delete") 137 | self.delete_action.triggered.connect(self.delete_trial) 138 | 139 | self.feasibility_separator = self.menu.addAction("---- Feasibility Analysis ----") 140 | self.feasibility_separator.setEnabled(False) 141 | self.shorter_action = self.menu.addAction("Set Shorter Trial") 142 | self.shorter_action.triggered.connect(self.setAsShorterTrial) 143 | 144 | self.longer_action = self.menu.addAction("Set Longer Trial") 145 | self.longer_action.triggered.connect(self.setAsLongerTrial) 146 | 147 | 148 | if self.current_trial.end_date is None: 149 | self.shorter_action.setEnabled(False) 150 | self.longer_action.setEnabled(False) 151 | else: 152 | self.shorter_action.setEnabled(True) 153 | self.longer_action.setEnabled(True) 154 | 155 | table_viewport = self.trial_table.viewport() 156 | self.menu.popup(table_viewport.mapToGlobal(pos)) 157 | 158 | def setAsShorterTrial(self): 159 | self.emit(QtCore.SIGNAL("shorter_trial_set(PyQt_PyObject)"), self.current_trial) 160 | 161 | def setAsLongerTrial(self): 162 | self.emit(QtCore.SIGNAL("longer_trial_set(PyQt_PyObject)"), self.current_trial) 163 | 164 | 165 | def update_current_experiment(self, index): 166 | self.current_experiment = self.experiment_list.currentItem() 167 | #self.experiment_name.setText(self.current_experiment.name) 168 | self.update_trial_table() 169 | # self.trial_table.setSelection() 170 | 171 | 172 | def update_current_trial(self,x,y): 173 | self.current_trial = self.trial_table_selection_model.currentIndex().data(QtCore.Qt.EditRole) 174 | if len(x.indexes()) == 0: 175 | self.trial_status.start_trial_button.setEnabled(False) 176 | self.trial_status.edit_trial_button.setEnabled(False) 177 | self.trial_status.refresh_trial_button.setEnabled(False) 178 | self.trial_status.stop_trial_button.setEnabled(False) 179 | else: 180 | self.update_trial_details() 181 | 182 | def edit_trial(self): 183 | dialog = NewTrialDialog(self.session, experiment=self.current_experiment, parent=self, trial=self.current_trial) 184 | dialog.accepted.connect(self.trial_table_model.refresh) 185 | dialog.exec() 186 | 187 | 188 | 189 | def update_trial_details(self): 190 | self.session.refresh(self.current_trial) 191 | self.trial_status.edit_trial_button.setEnabled(True) 192 | self.trial_status.refresh_trial_button.setEnabled(True) 193 | 194 | if self.current_trial.start_date == None: 195 | self.trial_status.start_trial_button.setEnabled(True) 196 | self.trial_status.stop_trial_button.setEnabled(False) 197 | else: 198 | self.trial_status.start_trial_button.setEnabled(False) 199 | self.trial_status.stop_trial_button.setEnabled(True) 200 | 201 | if(self.current_trial.__class__.__name__ == "HTTPTrial"): 202 | self.echo_trial_details.hide() 203 | self.http_trial_details.show() 204 | self.http_trial_details.request_url.setText(self.current_trial.request_url) 205 | self.http_trial_details.type.setText("HTTP Trial") 206 | 207 | else: 208 | self.echo_trial_details.show() 209 | self.http_trial_details.hide() 210 | self.echo_trial_details.delay.setText(str(self.current_trial.delay)) 211 | self.echo_trial_details.type.setText("Echo Trial") 212 | 213 | self.http_trial_details.name.setText(self.current_trial.name) 214 | self.http_trial_details.description.setText(self.current_trial.description) 215 | 216 | self.racer_settings_widget.racer.setText(self.current_trial.racer.hostname) 217 | self.racer_settings_widget.core_id.setText(str(self.current_trial.core_id)) 218 | self.racer_settings_widget.real_time.setText(str(self.current_trial.real_time)) 219 | 220 | self.trial_status.start.setText(str(self.current_trial.start_date)) 221 | self.trial_status.end.setText(str(self.current_trial.end_date)) 222 | 223 | try: 224 | job = Job.fetch(self.current_trial.job, connection=self.redis_conn) 225 | self.trial_status.job_status.setText(job.get_status()) 226 | except: 227 | self.trial_status.job_status.setText("not scheduled") 228 | 229 | 230 | 231 | 232 | def stop_trial(self): 233 | self.current_trial.start_date = None 234 | self.current_trial.end_date = None 235 | self.session.add(self.current_trial) 236 | self.session.commit() 237 | 238 | job = Job.fetch(self.current_trial.job, connection=self.redis_conn) 239 | job.cancel() 240 | self.update_trial_details() 241 | self.trial_table.resizeColumnsToContents() 242 | 243 | 244 | def delete_trial(self): 245 | reply = QtGui.QMessageBox.question(self, "Confirm", "Really delete the selected trial?", QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) 246 | if reply == QtGui.QMessageBox.Yes: 247 | self.session.delete(self.current_trial) 248 | self.session.commit() 249 | self.update_trial_table() 250 | 251 | def duplicate_trial(self): 252 | new = self.current_trial.duplicate() 253 | self.session.add(new) 254 | self.session.commit() 255 | self.update_trial_table() 256 | 257 | def start_trial(self): 258 | q = Queue(self.current_trial.racer.hostname, connection=self.redis_conn) 259 | t = self.current_trial 260 | 261 | job = None 262 | if(self.current_trial.__class__.__name__ == "HTTPTrial"): 263 | job = HTTPTrialJob() 264 | job.request = t.request 265 | job.request_url = t.request_url 266 | else: 267 | job = EchoTrialJob() 268 | job.target_host = t.host 269 | job.target_port = t.port 270 | job.delay = t.delay 271 | 272 | job.reps = t.reps 273 | job.core_affinity = t.core_id 274 | if t.real_time: 275 | job.real_time = 1 276 | else: 277 | job.real_time = 0 278 | 279 | 280 | res = q.enqueue_call(func=execute_trial, args=(job,), result_ttl=-1, timeout=1000000) 281 | self.current_trial.job = res.get_id() 282 | self.current_trial.start_date = datetime.now() 283 | self.session.add(self.current_trial) 284 | self.session.commit() 285 | 286 | res.save() 287 | self.trial_status.start_trial_button.setEnabled(False) 288 | self.trial_status.stop_trial_button.setEnabled(True) 289 | self.update_trial_details() 290 | self.trial_table.resizeColumnsToContents() 291 | 292 | 293 | def update_trial_table(self): 294 | self.trial_table_model.setFilter(Trial.experiment==self.current_experiment) 295 | self.trial_table.resizeColumnsToContents() 296 | 297 | 298 | 299 | def new_trial(self): 300 | dialog = NewTrialDialog(self.session, experiment=self.current_experiment, parent=self) 301 | dialog.accepted.connect(self.trial_table_model.refresh) 302 | dialog.exec() 303 | 304 | 305 | def new_experiment(self): 306 | dialog = QtGui.QInputDialog(self) 307 | dialog.setLabelText("Please enter the name for the new Experiment.") 308 | dialog.textValueSelected.connect(self.store_new_experiment) 309 | dialog.exec() 310 | 311 | def store_new_experiment(self, name): 312 | exp = Experiment() 313 | exp.name = name 314 | self.session.add(exp) 315 | self.session.commit() 316 | self.experiment_list.refresh_experiments() 317 | 318 | -------------------------------------------------------------------------------- /time_trial_gui/gui/feasibility_tab.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | import pickle 3 | from gui.trial_detail_widget import EchoTrialDetailsWidget 4 | from lib.racer_driver import execute_trial 5 | from lib.trial_jobs import TrialJob, EchoTrialJob 6 | 7 | __author__ = 'daniel' 8 | from gui.experiment_combo_box import ExperimentComboBox 9 | from gui.new_trial_dialog import NewTrialDialog 10 | from gui.sqlalchemy_table_model import SQLAlchemyTableModel 11 | from models.trial import Trial 12 | from models.experiment import Experiment 13 | 14 | from rq import Connection, Queue 15 | from redis import Redis 16 | 17 | 18 | 19 | 20 | from PyQt4 import QtGui, QtCore 21 | from gui.data_source_model import DataSourceModel 22 | from gui.plot_style_edit_dialog import PlotStyleEditDialog 23 | from gui.plotter_widget import PlotterWidget 24 | 25 | 26 | 27 | from lib.timing_data import TimingData 28 | from lib.plot import Plot 29 | from lib.box_test import BoxTest 30 | 31 | class FeasibilityTab(QtGui.QWidget): 32 | 33 | longer_plot = None 34 | shorter_plot = None 35 | 36 | def __init__(self, session = None, parent = None): 37 | super(FeasibilityTab, self).__init__(parent) 38 | self.session = session 39 | 40 | self.current_trial = None 41 | 42 | self.layout = QtGui.QGridLayout() 43 | self.setLayout(self.layout) 44 | 45 | 46 | #################### 47 | ## ANALYSIS 48 | #################### 49 | self.analysis_box = QtGui.QGroupBox(self, title="Analysis") 50 | self.layout.addWidget(self.analysis_box,1,0, 1, 1) 51 | self.analysis_box_layout = QtGui.QGridLayout(self.analysis_box) 52 | 53 | self.analysis_box.setLayout(self.analysis_box_layout) 54 | 55 | ### shorter trial box 56 | self.analysis_data_source_shorter = QtGui.QGroupBox("Shorter Trial") 57 | self.analysis_data_source_shorter_layout = QtGui.QGridLayout() 58 | self.analysis_data_source_shorter.setLayout(self.analysis_data_source_shorter_layout) 59 | 60 | self.shorter_trial_label = QtGui.QLabel("Trial:") 61 | self.shorter_trial = QtGui.QLabel("[select on experiment tab]") 62 | self.analysis_data_source_shorter_layout.addWidget(self.shorter_trial_label, 0, 0) 63 | self.analysis_data_source_shorter_layout.addWidget(self.shorter_trial, 0, 1) 64 | 65 | self.shorter_trial_color_label = QtGui.QLabel("Color:") 66 | self.shorter_trial_color = QtGui.QLabel() 67 | self.analysis_data_source_shorter_layout.addWidget(self.shorter_trial_color_label, 1, 0) 68 | self.analysis_data_source_shorter_layout.addWidget(self.shorter_trial_color, 1, 1) 69 | 70 | 71 | self.shorter_trial_bins_label = QtGui.QLabel("Bins:") 72 | self.shorter_trial_bins = QtGui.QLabel() 73 | self.analysis_data_source_shorter_layout.addWidget(self.shorter_trial_bins_label, 2, 0) 74 | self.analysis_data_source_shorter_layout.addWidget(self.shorter_trial_bins, 2, 1) 75 | 76 | 77 | self.shorter_trial_edit = QtGui.QPushButton("Edit") 78 | self.shorter_trial_edit.setEnabled(False) 79 | self.shorter_trial_edit.released.connect(self.edit_shorter_trial) 80 | self.analysis_data_source_shorter_layout.addWidget(self.shorter_trial_edit, 3, 0, 1, 2) 81 | 82 | self.analysis_box_layout.addWidget(self.analysis_data_source_shorter, 0, 0) 83 | 84 | 85 | 86 | ### longer trial box 87 | self.analysis_data_source_longer = QtGui.QGroupBox("Longer Trial") 88 | self.analysis_data_source_longer_layout = QtGui.QGridLayout() 89 | self.analysis_data_source_longer.setLayout(self.analysis_data_source_longer_layout) 90 | 91 | self.longer_trial_label = QtGui.QLabel("Trial:") 92 | self.longer_trial = QtGui.QLabel("[select on experiment tab]") 93 | self.analysis_data_source_longer_layout.addWidget(self.longer_trial_label, 0, 0) 94 | self.analysis_data_source_longer_layout.addWidget(self.longer_trial, 0, 1) 95 | 96 | self.longer_trial_color_label = QtGui.QLabel("Color:") 97 | self.longer_trial_color = QtGui.QLabel() 98 | self.analysis_data_source_longer_layout.addWidget(self.longer_trial_color_label, 1, 0) 99 | self.analysis_data_source_longer_layout.addWidget(self.longer_trial_color, 1, 1) 100 | 101 | 102 | self.longer_trial_bins_label = QtGui.QLabel("Bins:") 103 | self.longer_trial_bins = QtGui.QLabel() 104 | self.analysis_data_source_longer_layout.addWidget(self.longer_trial_bins_label, 2, 0) 105 | self.analysis_data_source_longer_layout.addWidget(self.longer_trial_bins, 2, 1) 106 | 107 | 108 | self.longer_trial_edit = QtGui.QPushButton("Edit") 109 | self.longer_trial_edit.setEnabled(False) 110 | self.longer_trial_edit.released.connect(self.edit_longer_trial) 111 | self.analysis_data_source_longer_layout.addWidget(self.longer_trial_edit, 3, 0, 1, 2) 112 | 113 | 114 | 115 | self.analysis_box_layout.addWidget(self.analysis_data_source_longer, 0, 1) 116 | 117 | 118 | 119 | #### configuration 120 | self.analysis_config = QtGui.QGroupBox("Analysis Configuration") 121 | self.analysis_config_layout = QtGui.QGridLayout() 122 | 123 | self.analysis_config.setLayout(self.analysis_config_layout) 124 | 125 | self.analysis_type_label = QtGui.QLabel("Analysis Method") 126 | self.analysis_type = QtGui.QComboBox() 127 | self.analysis_type.addItem("Box Test") 128 | self.analysis_config_layout.addWidget(self.analysis_type_label, 0, 0) 129 | self.analysis_config_layout.addWidget(self.analysis_type, 0, 1) 130 | 131 | 132 | self.analysis_lower_quantile_label = QtGui.QLabel("Lower Quantile") 133 | self.analysis_lower_quantile = QtGui.QLineEdit(text="6") 134 | self.analysis_config_layout.addWidget(self.analysis_lower_quantile_label, 1, 0) 135 | self.analysis_config_layout.addWidget(self.analysis_lower_quantile, 1, 1) 136 | 137 | self.analysis_upper_quantile_label = QtGui.QLabel("Upper Quantile") 138 | self.analysis_upper_quantile = QtGui.QLineEdit(text="6.5") 139 | self.analysis_config_layout.addWidget(self.analysis_upper_quantile_label, 2, 0) 140 | self.analysis_config_layout.addWidget(self.analysis_upper_quantile, 2, 1) 141 | 142 | 143 | 144 | self.analysis_perfom_button = QtGui.QPushButton("Perform Analysis") 145 | self.analysis_perfom_button.released.connect(self.perform_analysis) 146 | self.analysis_config_layout.addWidget(self.analysis_perfom_button, 3, 0, 1, 2) 147 | 148 | self.analysis_box_layout.addWidget(self.analysis_config, 0,2) 149 | 150 | # Reset 151 | self.reset_plot_button = QtGui.QPushButton("Reset Plot") 152 | self.reset_plot_button.released.connect(self.reset_plot) 153 | self.analysis_box_layout.addWidget(self.reset_plot_button, 1, 0,1, 2) 154 | 155 | ## Result 156 | self.analysis_result_box = QtGui.QGroupBox("Result") 157 | self.analysis_result_box_layout = QtGui.QGridLayout() 158 | self.analysis_result_box.setLayout(self.analysis_result_box_layout) 159 | 160 | self.analysis_result = QtGui.QLabel() 161 | self.analysis_result_box_layout.addWidget(self.analysis_result, 0, 0) 162 | 163 | self.analysis_box_layout.addWidget(self.analysis_result_box, 1,2, 1, 1) 164 | 165 | 166 | ### PLotter 167 | self.plotter = PlotterWidget(self) 168 | # self.plotter.set_data_source_model(self.data_source_model) 169 | self.analysis_box_layout.addWidget(self.plotter, 2,0,1,3) 170 | 171 | # self.data_source_model.rowsInserted.connect(self.plotter.update_plot) 172 | 173 | def reset_plot(self): 174 | self.shorter_trial.setText("[seect on experiment tab]") 175 | self.shorter_trial_color.setText("") 176 | self.shorter_trial_bins.setText("") 177 | 178 | self.longer_trial.setText("[select on experiment tab]") 179 | self.longer_trial_color.setText("") 180 | self.longer_trial_bins.setText("") 181 | 182 | self.shorter_trial_edit.setEnabled(False) 183 | self.longer_trial_edit.setEnabled(False) 184 | 185 | self.plotter.reset() 186 | 187 | def edit_shorter_trial(self): 188 | dialog = PlotStyleEditDialog(plot = self.shorter_plot) 189 | dialog.accepted.connect(self.update_plot_settings_view) 190 | dialog.exec() 191 | 192 | def edit_longer_trial(self): 193 | dialog = PlotStyleEditDialog(plot = self.longer_plot) 194 | dialog.accepted.connect(self.update_plot_settings_view) 195 | dialog.exec() 196 | 197 | def update_plot_settings_view(self): 198 | self.plotter.update_plot() 199 | 200 | if self.shorter_plot is not None: 201 | self.shorter_trial_bins.setText(str(self.shorter_plot.bins)) 202 | self.shorter_trial_color.setText(str(self.shorter_plot.color)) 203 | 204 | if self.longer_plot is not None: 205 | self.longer_trial_bins.setText(str(self.longer_plot.bins)) 206 | self.longer_trial_color.setText(str(self.longer_plot.color)) 207 | 208 | 209 | 210 | 211 | def set_shorter(self, trial): 212 | self.shorter_trial.setText(trial.name) 213 | self.shorter_trial_data = trial 214 | 215 | self.shorter = TimingData() 216 | self.shorter.parse_csv(self.shorter_trial_data.result) 217 | self.shorter_plot = Plot(self.shorter) 218 | self.shorter_plot.color = 'blue' 219 | self.shorter_plot.label = trial.name 220 | self.plotter.add_plot(self.shorter_plot) 221 | 222 | self.shorter_trial_edit.setEnabled(True) 223 | self.update_plot_settings_view() 224 | 225 | def set_longer(self, trial): 226 | self.longer_trial.setText(trial.name) 227 | self.longer_trial_data = trial 228 | 229 | self.longer = TimingData() 230 | self.longer.parse_csv(self.longer_trial_data.result) 231 | self.longer_plot = Plot(self.longer) 232 | self.longer_plot.color = 'red' 233 | self.longer_plot.label = trial.name 234 | 235 | self.plotter.add_plot(self.longer_plot) 236 | 237 | self.longer_trial_edit.setEnabled(True) 238 | self.update_plot_settings_view() 239 | 240 | 241 | def perform_analysis(self): 242 | self.plotter.update_plot() 243 | box_test = BoxTest(self.shorter, self.longer, float(self.analysis_lower_quantile.text()), float(self.analysis_upper_quantile.text())) 244 | res = box_test.perform() 245 | self.analysis_result.setText("Are the two distributions distinct? " + str(res)) 246 | x_box = box_test.x_box() 247 | y_box = box_test.y_box() 248 | 249 | self.plotter.plot_canvas.draw_rectangle(x_box[0], 0, x_box[1] - x_box[0], 1e7, fg_color=self.shorter_plot.color, edge_color="black") 250 | self.plotter.plot_canvas.draw_rectangle(y_box[0], 0, y_box[1] - y_box[0], 1e7, fg_color=self.longer_plot.color, edge_color="black") 251 | 252 | 253 | # def event_open_data_source_edit(self, index): 254 | # dialog = EditDataSourceDialog(index.data(QtCore.Qt.EditRole), self) 255 | # dialog.accepted.connect(self.event_data_source_edited) 256 | # dialog.exec() 257 | 258 | def event_data_source_edited(self): 259 | self.data_source_table.resizeColumnsToContents() 260 | self.plotter.update_plot() 261 | 262 | def event_show_select_file_dialog(self): 263 | file_dialog = QtGui.QFileDialog() 264 | file_dialog.setAcceptMode(QtGui.QFileDialog.AcceptOpen) 265 | filters = [ "PEM Files (*.pem)", "Any files (*)" ] 266 | # file_dialog.fileSelected.connect(self.event_file_selected) 267 | file_dialog.filesSelected.connect(self.event_files_selected) 268 | file_dialog.setFileMode(QtGui.QFileDialog.ExistingFiles) 269 | file_dialog.exec() 270 | 271 | def event_files_selected(self, file_names): 272 | print(file_names) 273 | for f in file_names: 274 | self.event_file_selected(f) 275 | 276 | def event_file_selected(self,file_name): 277 | new_data = TimingData() 278 | new_data.load_from_csv(file_name) 279 | new_plot = Plot(new_data) 280 | self.data_source_model.add_data(new_plot) 281 | self.data_source_table.resizeColumnsToContents() 282 | 283 | #data = parse_csv(file_name) 284 | #self.plot_canvas.add_plot(data, 200, [min(data), 26*1000*1000], "100 micros", 'red') 285 | #self.plot_canvas.update_figure() 286 | 287 | def add_data_row(self, data): 288 | pass 289 | 290 | 291 | -------------------------------------------------------------------------------- /time_trial_gui/gui/histogram.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from gui.mpl_canvas import * 4 | from pylab import Rectangle 5 | from matplotlib.patches import Rectangle 6 | import matplotlib.ticker as ticker 7 | 8 | import numpy 9 | 10 | from PyQt4 import QtGui, QtCore 11 | 12 | 13 | 14 | class Histogram(MplCanvas): 15 | 16 | def __init__(self, parent=None, width=5, height=4, dpi=100, settings=None): 17 | MplCanvas.__init__(self, parent, width, height, dpi) 18 | 19 | self.settings = settings 20 | self.logger = logging.getLogger('time_trial') 21 | 22 | def add_plot_raw(self, data, bins, type, range, label, color, final = False): 23 | self.axes.hist(data, bins, histtype=type, range=range, color=color, label=label, alpha=0.5) 24 | self.axes.relim() 25 | if self.settings.legend: 26 | self.axes.legend() 27 | 28 | 29 | def compute_initial_figure(self): 30 | pass 31 | 32 | def draw_rectangle(self, x, y, width, height, fg_color="CornflowerBlue", edge_color="gray"): 33 | self.axes.add_patch(Rectangle((x, y), width, height, ec=edge_color, fc=fg_color, alpha=0.5, zorder=10)) 34 | self.update_figure() 35 | 36 | 37 | def add_plot(self, plot): 38 | ticks = ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x*self.settings.x_scaling)) 39 | self.axes.xaxis.set_major_formatter(ticks) 40 | 41 | if plot.minimum is None and plot.maximum is None: 42 | range = [min(plot.timing_data.data), max(plot.timing_data.data)] 43 | elif plot.range_type == "absolute": 44 | range = [plot.minimum, plot.maximum] 45 | print(range) 46 | if len(numpy.intersect1d(range, plot.timing_data.data)) == 0: 47 | msg = QtGui.QMessageBox() 48 | msg.setIcon(QtGui.QMessageBox.Warning) 49 | msg.setText("The specified range is invalid for data set as they do not overlap. Falling back to plotting the full range instead:\n [min(data), max(data)].") 50 | msg.exec() 51 | logging.warning("Specified range is invalid for data. Falling back to [min(data), max(data)].") 52 | plot.minimum = min(plot.timing_data.data) 53 | plot.maximum = max(plot.timing_data.data) 54 | range = [plot.minimum, plot.maximum] 55 | 56 | else: 57 | range = [plot.timing_data.quantile(plot.minimum), plot.timing_data.quantile(plot.maximum)] 58 | 59 | self.add_plot_raw(plot.timing_data.data, plot.bins, plot.style, range, plot.label, plot.color) 60 | 61 | def clear(self): 62 | self.axes.cla() 63 | self.axes.set_xlabel(self.settings.x_axis_label) 64 | self.axes.set_ylabel(self.settings.y_axis_label) 65 | self.fig.tight_layout() 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /time_trial_gui/gui/http_request_text_edit.py: -------------------------------------------------------------------------------- 1 | from PyQt4 import QtGui 2 | from pygments import highlight, lex 3 | from pygments.lexers import HttpLexer 4 | from pygments.lexers import guess_lexer 5 | from pygments.formatters import HtmlFormatter 6 | import re 7 | 8 | 9 | class HttpRequestTextEdit(QtGui.QTextEdit): 10 | 11 | def __init__(self, parent): 12 | super(HttpRequestTextEdit, self).__init__(parent) 13 | self.setMinimumWidth(600) 14 | self.setMinimumHeight(400) 15 | self.setAcceptRichText(False) 16 | self.setTabChangesFocus(True) 17 | 18 | 19 | 20 | 21 | def focusOutEvent(self, QFocusEvent): 22 | self.highlight() 23 | 24 | def highlight(self): 25 | request = self.toPlainText() 26 | highlighted = highlight(request, HttpLexer(), HtmlFormatter(full=True, style="friendly")) 27 | self.setHtml(highlighted) 28 | 29 | -------------------------------------------------------------------------------- /time_trial_gui/gui/mpl_canvas.py: -------------------------------------------------------------------------------- 1 | from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas 2 | from matplotlib.figure import Figure 3 | from PyQt4 import QtGui, QtCore 4 | 5 | class MplCanvas(FigureCanvas): 6 | """Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.).""" 7 | def __init__(self, parent=None, width=5, height=4, dpi=100): 8 | self.fig = Figure(figsize=(width, height), dpi=dpi) 9 | self.axes = self.fig.add_subplot(111) 10 | self.axes.autoscale(enable=True, axis='y', tight=True) 11 | # We want the axes cleared every time plot() is called 12 | # self.axes.hold(True) 13 | 14 | 15 | # 16 | FigureCanvas.__init__(self, self.fig) 17 | self.setParent(parent) 18 | 19 | FigureCanvas.setSizePolicy(self, 20 | QtGui.QSizePolicy.Expanding, 21 | QtGui.QSizePolicy.Expanding) 22 | FigureCanvas.updateGeometry(self) 23 | 24 | def update_figure(self): 25 | self.draw() 26 | FigureCanvas.updateGeometry(self) 27 | 28 | -------------------------------------------------------------------------------- /time_trial_gui/gui/new_trial_dialog.py: -------------------------------------------------------------------------------- 1 | from gui.http_request_text_edit import HttpRequestTextEdit 2 | from models.racer import Racer 3 | from models.trial import Trial, EchoTrial, HTTPTrial 4 | 5 | 6 | __author__ = 'daniel' 7 | 8 | 9 | 10 | from PyQt4 import QtGui, QtCore 11 | 12 | class NewTrialDialog(QtGui.QDialog): 13 | init_done = False 14 | 15 | def __init__(self, session, experiment = None, trial = None, parent = None, flags = QtCore.Qt.Dialog): 16 | QtGui.QDialog.__init__(self, parent, flags) 17 | 18 | self.session = session 19 | self.experiment = experiment 20 | self.trial = trial 21 | 22 | self.layout = QtGui.QGridLayout() 23 | self.setLayout(self.layout) 24 | 25 | top_layout = QtGui.QFormLayout() 26 | self.type = QtGui.QComboBox() 27 | self.type.currentIndexChanged.connect(self.trial_type_changed) 28 | self.type.addItem("HTTP Trial", "HTTP Trial") 29 | self.type.addItem("Echo Trial", "Echo Trial") 30 | top_layout.addRow("Trial Type", self.type) 31 | 32 | 33 | self.layout.addLayout(top_layout,0,0,1,2) 34 | 35 | ## GENERAL 36 | 37 | general_group = QtGui.QGroupBox("General Settings") 38 | general_group_layout = QtGui.QFormLayout() 39 | general_group.setLayout(general_group_layout) 40 | 41 | 42 | 43 | self.name = QtGui.QLineEdit() 44 | if self.trial is not None: self.name.setText(trial.name) 45 | general_group_layout.addRow("Name", self.name) 46 | 47 | self.reps = QtGui.QLineEdit() 48 | if self.trial is not None: self.reps.setText(str(trial.reps)) 49 | general_group_layout.addRow("Repetitions", self.reps) 50 | 51 | self.description = QtGui.QPlainTextEdit() 52 | self.description.setMinimumWidth(100) 53 | self.description.setTabChangesFocus(True) 54 | if self.trial is not None: self.description.setPlainText(trial.description) 55 | 56 | general_group_layout.addRow("Description", self.description) 57 | 58 | 59 | self.layout.addWidget(general_group,1,0) 60 | 61 | racer_group = QtGui.QGroupBox("Racer Settings") 62 | racer_group_layout = QtGui.QFormLayout() 63 | racer_group.setLayout(racer_group_layout) 64 | 65 | self.racer = QtGui.QComboBox() 66 | for r in self.session.query(Racer).all(): 67 | self.racer.addItem(r.hostname, r.id) 68 | 69 | if self.trial is not None: self.racer.setCurrentIndex(self.racer.findData(self.trial.racer.id)) 70 | 71 | racer_group_layout.addRow("Racer", self.racer) 72 | 73 | self.core_id = QtGui.QLineEdit() 74 | if self.trial is not None: self.core_id.setText(str(self.trial.core_id)) 75 | 76 | self.core_id.setToolTip("Assigns the racer to the specified CPU core.") 77 | racer_group_layout.addRow("CPU Core", self.core_id) 78 | 79 | self.real_time = QtGui.QComboBox() 80 | self.real_time.setToolTip("Executes the trial with real-time priority.") 81 | self.real_time.addItem("True", userData=True) 82 | self.real_time.addItem("False", userData=False) 83 | if self.trial is not None: self.real_time.setCurrentIndex(self.real_time.findData(bool(self.trial.real_time))) 84 | racer_group_layout.addRow("Real-Time Scheduling", self.real_time) 85 | 86 | self.layout.addWidget(racer_group, 2,0) 87 | 88 | 89 | ## ECHO 90 | self.echo_group = QtGui.QGroupBox("Echo-Specific Settings") 91 | echo_group_layout = QtGui.QFormLayout() 92 | self.echo_group.setLayout(echo_group_layout) 93 | 94 | self.host = QtGui.QLineEdit() 95 | echo_group_layout.addRow("Host", self.host) 96 | 97 | self.port = QtGui.QLineEdit() 98 | echo_group_layout.addRow("Port", self.port) 99 | 100 | self.delay = QtGui.QLineEdit() 101 | echo_group_layout.addRow("Delay (ns)", self.delay) 102 | 103 | if self.trial is not None and self.trial.discriminator == "Echo Trial": 104 | self.type.setCurrentIndex(self.type.findData("Echo Trial")) 105 | self.type.setEnabled(False) # we don't allow chanigng the type when editing 106 | self.host.setText(self.trial.host) 107 | self.port.setText(str(self.trial.port)) 108 | self.delay.setText(str(self.trial.delay)) 109 | 110 | 111 | self.layout.addWidget(self.echo_group,1,1,2,1) 112 | self.echo_group.hide() 113 | 114 | 115 | ## HTTP 116 | self.http_group = QtGui.QGroupBox("HTTP-Specific Settings") 117 | http_group_layout = QtGui.QFormLayout() 118 | self.http_group.setLayout(http_group_layout) 119 | 120 | 121 | if self.trial is not None and self.trial.discriminator == "Echo Trial": 122 | self.echo_group.show() 123 | self.http_group.hide() 124 | else: 125 | self.echo_group.hide() 126 | self.http_group.show() 127 | 128 | 129 | self.request_url = QtGui.QLineEdit() 130 | self.request_url.setMinimumWidth(400) 131 | http_group_layout.addRow("Request URL", self.request_url) 132 | http_group_layout.addRow("",QtGui.QLabel(text="(e.g., https://www.example.com:43562)")) 133 | 134 | self.http_request = HttpRequestTextEdit(self) 135 | http_group_layout.addRow("HTTP request",self.http_request) 136 | 137 | self.layout.addWidget(self.http_group, 1,1, 2,1) 138 | 139 | if self.trial is not None and self.trial.discriminator == "HTTP Trial": 140 | self.type.setCurrentIndex(self.type.findData("HTTP Trial")) 141 | self.type.setEnabled(False) # we don't allow chanigng the type when editing 142 | self.request_url.setText(self.trial.request_url) 143 | self.http_request.setPlainText(self.trial.request) 144 | self.http_request.highlight() 145 | 146 | 147 | button_box = QtGui.QDialogButtonBox() 148 | if self.trial is None: 149 | button_box.addButton("Create", QtGui.QDialogButtonBox.AcceptRole) 150 | else: 151 | button_box.addButton("Save", QtGui.QDialogButtonBox.AcceptRole) 152 | 153 | button_box.addButton("Cancel", QtGui.QDialogButtonBox.RejectRole) 154 | button_box.rejected.connect(self.cancel) 155 | button_box.accepted.connect(self.store) 156 | 157 | self.layout.addWidget(button_box,3,0, 1, 2) 158 | self.init_done = True 159 | 160 | def trial_type_changed(self, index): 161 | if self.init_done: 162 | 163 | if self.type.currentText() == "HTTP Trial": 164 | self.echo_group.hide() 165 | self.http_group.show() 166 | else: 167 | self.echo_group.show() 168 | self.http_group.hide() 169 | 170 | 171 | 172 | 173 | def store(self): 174 | 175 | ## init for editing 176 | trial = self.trial 177 | 178 | # only init a new Trial object when not editing 179 | if trial is None: 180 | if self.type.currentText() == "HTTP Trial": 181 | trial = HTTPTrial() 182 | else: 183 | trial = EchoTrial() 184 | 185 | 186 | #store type-specific data 187 | if self.type.currentText() == "HTTP Trial": 188 | trial.request_url = self.request_url.text() 189 | trial.request = self.http_request.toPlainText() 190 | else: 191 | trial.delay = self.delay.text() 192 | trial.host = self.host.text() 193 | trial.port = self.port.text() 194 | 195 | trial.name = self.name.text() 196 | trial.description = self.description.toPlainText() 197 | trial.reps = self.reps.text() 198 | 199 | trial.core_id = int(self.core_id.text()) 200 | trial.real_time = self.real_time.itemData(self.real_time.currentIndex(), QtCore.Qt.UserRole) 201 | racer = self.session.query(Racer).filter_by(hostname = self.racer.currentText()).first() 202 | 203 | trial.racer = racer 204 | trial.experiment = self.experiment 205 | self.session.add(trial) 206 | self.session.commit() 207 | self.accept() 208 | 209 | def cancel(self): 210 | self.reject() 211 | 212 | 213 | 214 | 215 | -------------------------------------------------------------------------------- /time_trial_gui/gui/plot_settings_dialog.py: -------------------------------------------------------------------------------- 1 | __author__ = 'daniel' 2 | from PyQt4 import QtGui, QtCore 3 | 4 | class PlotSettingsDialog(QtGui.QDialog): 5 | 6 | def __init__(self, plot_settings=None, parent=None): 7 | super(PlotSettingsDialog, self).__init__(parent) 8 | 9 | self.layout = QtGui.QVBoxLayout() 10 | self.setLayout(self.layout) 11 | self.plot_settings = plot_settings 12 | 13 | 14 | form_layout = QtGui.QFormLayout() 15 | 16 | # plot settings 17 | plot_settings_box = QtGui.QGroupBox(title="Plot Settings") 18 | plot_settings_box_layout = QtGui.QVBoxLayout() 19 | plot_settings_box.setLayout(plot_settings_box_layout) 20 | plot_settings_box_form_layout = QtGui.QFormLayout() 21 | plot_settings_box_layout.addLayout(plot_settings_box_form_layout) 22 | 23 | self.plot_settings_box_x_axis_label = QtGui.QLineEdit(text = self.plot_settings.x_axis_label) 24 | form_layout.addRow("X-Axis Label", self.plot_settings_box_x_axis_label) 25 | 26 | self.plot_settings_box_y_axis_label = QtGui.QLineEdit(text = self.plot_settings.y_axis_label) 27 | form_layout.addRow("Y-Axis Label", self.plot_settings_box_y_axis_label) 28 | 29 | self.plot_settings_box_legend = QtGui.QCheckBox() 30 | if self.plot_settings.legend: 31 | self.plot_settings_box_legend.setChecked(True) 32 | else: 33 | self.plot_settings_box_legend.setChecked(False) 34 | 35 | 36 | self.x_minimum = QtGui.QLineEdit(text = self.plot_settings.x_minimum) 37 | form_layout.addRow("X-Axis Minimum", self.x_minimum) 38 | 39 | self.x_maximum = QtGui.QLineEdit(text = self.plot_settings.x_maximum) 40 | form_layout.addRow("X-Axis Maximum", self.x_maximum) 41 | 42 | 43 | self.x_scaling = QtGui.QLineEdit(text = str(self.plot_settings.x_scaling)) 44 | form_layout.addRow("X-Axis Scaling Factor", self.x_scaling) 45 | 46 | 47 | self.y_minimum = QtGui.QLineEdit(text = self.plot_settings.y_minimum) 48 | form_layout.addRow("Y-Axis Minimum", self.y_minimum) 49 | 50 | self.y_maximum = QtGui.QLineEdit(text = self.plot_settings.y_maximum) 51 | form_layout.addRow("Y-Axis Maximum", self.y_maximum) 52 | 53 | 54 | form_layout.addRow("Legend", self.plot_settings_box_legend) 55 | 56 | 57 | 58 | self.layout.addLayout(form_layout) 59 | button_box = QtGui.QDialogButtonBox() 60 | button_box.addButton("OK", QtGui.QDialogButtonBox.AcceptRole) 61 | button_box.addButton("Cancel", QtGui.QDialogButtonBox.RejectRole) 62 | button_box.rejected.connect(self.cancel) 63 | button_box.accepted.connect(self.apply) 64 | 65 | self.layout.addWidget(button_box) 66 | 67 | def apply(self): 68 | self.plot_settings.x_axis_label = self.plot_settings_box_x_axis_label.text() 69 | self.plot_settings.y_axis_label = self.plot_settings_box_y_axis_label.text() 70 | self.plot_settings.x_minimum = float(self.x_minimum.text()) if self.x_minimum.text()!= "" else None 71 | self.plot_settings.x_maximum = float(self.x_maximum.text()) if self.x_maximum.text()!= "" else None 72 | self.plot_settings.y_minimum = float(self.y_minimum.text()) if self.y_minimum.text()!= "" else None 73 | self.plot_settings.y_maximum = float(self.y_maximum.text()) if self.y_maximum.text()!= "" else None 74 | self.plot_settings.x_scaling = float(self.x_scaling.text()) if self.x_scaling.text()!= "" else None 75 | self.plot_settings.legend = self.plot_settings_box_legend.isChecked() 76 | self.accept() 77 | 78 | def cancel(self): 79 | self.reject() 80 | -------------------------------------------------------------------------------- /time_trial_gui/gui/plot_style_edit_dialog.py: -------------------------------------------------------------------------------- 1 | from PyQt4 import QtGui, QtCore 2 | 3 | class PlotStyleEditDialog(QtGui.QDialog): 4 | 5 | def __init__(self, plot, parent = None, flags = QtCore.Qt.Dialog): 6 | QtGui.QDialog.__init__(self, parent, flags) 7 | 8 | self.plot = plot 9 | 10 | self.layout = QtGui.QVBoxLayout() 11 | self.setLayout(self.layout) 12 | 13 | form_layout = QtGui.QFormLayout() 14 | self.layout.addLayout(form_layout) 15 | 16 | self.label = QtGui.QLineEdit(text=plot.label) 17 | form_layout.addRow("Label", self.label) 18 | self.bins = QtGui.QLineEdit(text=str(plot.bins)) 19 | form_layout.addRow("Bins",self.bins) 20 | self.color = QtGui.QLineEdit(text=plot.color) 21 | form_layout.addRow("Color",self.color) 22 | 23 | self.style = QtGui.QComboBox() 24 | self.style.insertItem(0, "Steps - Filled", "stepfilled") 25 | self.style.insertItem(0, "Steps", "step") 26 | self.style.insertItem(0, "Bars", "bar") 27 | form_layout.addRow("Style",self.style) 28 | 29 | 30 | self.filter_box = QtGui.QGroupBox("Plot Data Range",parent=self) 31 | self.layout.addWidget(self.filter_box) 32 | filter_box_layout = QtGui.QFormLayout() 33 | self.filter_box.setLayout(filter_box_layout) 34 | filter_comment = QtGui.QLabel(text="These settings will only affect plotting.") 35 | filter_comment.setWordWrap(True) 36 | filter_box_layout.addWidget(filter_comment) 37 | 38 | 39 | self.filter_type = QtGui.QComboBox() 40 | self.filter_type.addItem("Absolute Range", "absolute") 41 | self.filter_type.addItem("Percentile Range", "percentile") 42 | 43 | self.filter_type.setCurrentIndex(self.filter_type.findData(self.plot.range_type)) 44 | filter_box_layout.addWidget(self.filter_type) 45 | 46 | # for absolute 47 | self.minimum = QtGui.QLineEdit(text="" if plot.minimum == None else str(plot.minimum) ) 48 | filter_box_layout.addRow("Minimum",self.minimum) 49 | 50 | self.maximum = QtGui.QLineEdit(text="" if plot.maximum == None else str(plot.maximum)) 51 | filter_box_layout.addRow("Maximum",self.maximum) 52 | 53 | 54 | button_layout = QtGui.QHBoxLayout() 55 | save = QtGui.QPushButton(text="Save") 56 | save.released.connect(self.save) 57 | button_layout.addWidget(save) 58 | cancel = QtGui.QPushButton(text="Cancel") 59 | cancel.released.connect(self.reject) 60 | button_layout.addWidget(cancel) 61 | 62 | self.layout.addLayout(button_layout) 63 | 64 | def save(self): 65 | self.plot.color = self.color.text() 66 | self.plot.bins = int(self.bins.text()) 67 | self.plot.label = self.label.text() 68 | self.plot.style = self.style.itemData(self.style.currentIndex()) 69 | self.plot.minimum = float(self.minimum.text()) if self.minimum.text() != "" else None 70 | self.plot.maximum = float(self.maximum.text()) if self.maximum.text() != "" else None 71 | self.plot.range_type = "absolute" if self.filter_type.currentText() == "Absolute Range" else "percentile" 72 | self.accept() 73 | 74 | 75 | -------------------------------------------------------------------------------- /time_trial_gui/gui/plotter_tab.py: -------------------------------------------------------------------------------- 1 | __author__ = 'daniel' 2 | 3 | import logging 4 | 5 | from PyQt4 import QtGui 6 | from gui.data_source_model import DataSourceModel 7 | from gui.plotter_widget import PlotterWidget 8 | 9 | 10 | 11 | from lib.timing_data import TimingData 12 | from lib.plot import Plot 13 | 14 | class PlotterTab(QtGui.QWidget): 15 | 16 | def __init__(self, parent = None): 17 | super(PlotterTab, self).__init__(parent) 18 | self.layout = QtGui.QGridLayout() 19 | self.setLayout(self.layout) 20 | 21 | # data sources 22 | self.data_box = QtGui.QGroupBox(self, title="Data Sources") 23 | self.layout.addWidget(self.data_box,0,0) 24 | 25 | data_box_layout = QtGui.QGridLayout(self.data_box) 26 | self.data_box.setLayout(data_box_layout) 27 | 28 | self.data_source_model = DataSourceModel() 29 | self.data_source_table = QtGui.QTableView() 30 | self.data_source_table.setModel(self.data_source_model) 31 | self.data_source_table.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) 32 | self.data_source_table.activated.connect(self.event_open_data_source_edit) 33 | 34 | data_box_layout.addWidget(self.data_source_table, 0, 0) 35 | 36 | 37 | self.plotter = PlotterWidget(self) 38 | self.plotter.set_data_source_model(self.data_source_model) 39 | self.layout.addWidget(self.plotter, 1,0,1,2) 40 | 41 | self.data_source_model.rowsInserted.connect(self.plotter.update_plot) 42 | 43 | 44 | # main buttons 45 | add_file_button = QtGui.QPushButton(self.data_box) 46 | add_file_button.setText("Add File") 47 | 48 | add_file_button.released.connect(self.event_show_select_file_dialog) 49 | self.layout.addWidget(add_file_button,0,1) 50 | 51 | def event_open_data_source_edit(self, index): 52 | dialog = EditDataSourceDialog(index.data(QtCore.Qt.EditRole), self.main_widget) 53 | dialog.accepted.connect(self.event_data_source_edited) 54 | dialog.exec() 55 | 56 | def event_data_source_edited(self): 57 | self.data_source_table.resizeColumnsToContents() 58 | self.update_plot() 59 | 60 | def event_show_select_file_dialog(self): 61 | file_dialog = QtGui.QFileDialog() 62 | file_dialog.setAcceptMode(QtGui.QFileDialog.AcceptOpen) 63 | filters = [ "PEM Files (*.pem)", "Any files (*)" ] 64 | # file_dialog.fileSelected.connect(self.event_file_selected) 65 | file_dialog.filesSelected.connect(self.event_files_selected) 66 | file_dialog.setFileMode(QtGui.QFileDialog.ExistingFiles) 67 | file_dialog.exec() 68 | 69 | def event_files_selected(self, file_names): 70 | print(file_names) 71 | for f in file_names: 72 | self.event_file_selected(f) 73 | 74 | def event_file_selected(self,file_name): 75 | new_data = TimingData() 76 | new_data.load_from_csv(file_name) 77 | new_plot = Plot(new_data) 78 | self.data_source_model.add_data(new_plot) 79 | self.data_source_table.resizeColumnsToContents() 80 | 81 | #data = parse_csv(file_name) 82 | #self.plot_canvas.add_plot(data, 200, [min(data), 26*1000*1000], "100 micros", 'red') 83 | #self.plot_canvas.update_figure() 84 | 85 | def add_data_row(self, data): 86 | pass 87 | 88 | -------------------------------------------------------------------------------- /time_trial_gui/gui/plotter_widget.py: -------------------------------------------------------------------------------- 1 | from gui.plot_settings_dialog import PlotSettingsDialog 2 | 3 | __author__ = 'daniel' 4 | 5 | from gui.histogram import Histogram 6 | from PyQt4 import QtGui, QtCore 7 | 8 | from lib.plot_settings import PlotSettings 9 | 10 | 11 | class PlotterWidget(QtGui.QWidget): 12 | 13 | plots = [] 14 | 15 | def __init__(self, parent=None): 16 | super(PlotterWidget, self).__init__(parent) 17 | 18 | self.layout = QtGui.QGridLayout() 19 | self.setLayout(self.layout) 20 | 21 | 22 | self.plot_settings = PlotSettings() 23 | 24 | #plot_canvas = MyStaticMplCanvas(self.main_widget, width=5, height=4, dpi=100) 25 | self.plot_canvas = Histogram(self, width=9, height=3, dpi=100, settings=self.plot_settings) 26 | self.plot_canvas.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) 27 | self.plot_canvas.customContextMenuRequested.connect(self.display_context_menu) 28 | 29 | 30 | 31 | self.layout.addWidget(self.plot_canvas,0,0) 32 | 33 | # self.plot_settings_button = QtGui.QPushButton("Plot Settings") 34 | self.plot_settings_dialog = PlotSettingsDialog(plot_settings=self.plot_settings) 35 | self.plot_settings_dialog.accepted.connect(self.apply_plot_settings) 36 | # self.plot_settings_button.released.connect(self.plot_settings_dialog.exec) 37 | # self.layout.addWidget(self.plot_settings_button) 38 | self.setToolTip("Right click for options.") 39 | 40 | def display_context_menu(self, pos): 41 | self. menu = QtGui.QMenu() 42 | 43 | self.edit_action = self.menu.addAction("Edit Plot Settings") 44 | self.edit_action.triggered.connect(self.plot_settings_dialog.exec) 45 | 46 | self.pdf_action = self.menu.addAction("Save as PDF") 47 | self.pdf_action.triggered.connect(self.save_as_pdf) 48 | 49 | self.menu.popup(self.mapToGlobal(pos)) 50 | 51 | def save_as_pdf(self): 52 | dialog = QtGui.QFileDialog(self) 53 | dialog.setDefaultSuffix("pdf") 54 | dialog.setWindowTitle("Save Figure as PDF...") 55 | dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) 56 | dialog.setFileMode(QtGui.QFileDialog.AnyFile) 57 | dialog.fileSelected.connect(self.save_as_pdf_to_file) 58 | dialog.exec() 59 | 60 | 61 | def save_as_pdf_to_file(self, filename): 62 | self.plot_canvas.fig.set_size_inches(9,3) 63 | pdf = self.plot_canvas.fig.savefig(filename=filename, format="pdf") 64 | 65 | def apply_plot_settings(self): 66 | self.plot_canvas.axes.set_xlim(self.plot_settings.x_minimum, self.plot_settings.x_maximum) 67 | self.plot_canvas.axes.set_ylim(self.plot_settings.y_minimum, self.plot_settings.y_maximum) 68 | self.update_plot() 69 | 70 | def set_data_source_model(self, model): 71 | self.data_source_model = model 72 | 73 | def add_plot(self, plot): 74 | self.plots.append(plot) 75 | self.update_plot() 76 | 77 | def reset(self): 78 | self.plots.clear() 79 | self.update_plot() 80 | 81 | 82 | def update_plot(self): 83 | self.plot_canvas.clear() 84 | for d in self.plots: 85 | self.plot_canvas.add_plot(d) 86 | 87 | self.plot_canvas.axes.set_xlim(self.plot_settings.x_minimum, self.plot_settings.x_maximum) 88 | self.plot_canvas.axes.set_ylim(self.plot_settings.y_minimum, self.plot_settings.y_maximum) 89 | self.plot_canvas.update_figure() 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /time_trial_gui/gui/racer_edit_dialog.py: -------------------------------------------------------------------------------- 1 | __author__ = 'daniel' 2 | 3 | from PyQt4 import QtGui, QtCore 4 | 5 | class RacerEditDialog(QtGui.QDialog): 6 | 7 | def __init__(self, racer, parent = None, flags = QtCore.Qt.Dialog): 8 | QtGui.QDialog.__init__(self, parent, flags) 9 | 10 | self.racer = racer 11 | 12 | self.layout = QtGui.QVBoxLayout() 13 | self.setLayout(self.layout) 14 | 15 | form_layout = QtGui.QFormLayout() 16 | self.layout.addLayout(form_layout) 17 | 18 | self.name = QtGui.QLineEdit(text=racer.name) 19 | form_layout.addRow("Name", self.name) 20 | self.hostname = QtGui.QLineEdit(text=racer.hostname) 21 | form_layout.addRow("Hostname",self.hostname) 22 | self.location = QtGui.QLineEdit(text=racer.location) 23 | form_layout.addRow("Location",self.location) 24 | 25 | button_layout = QtGui.QHBoxLayout() 26 | save = QtGui.QPushButton(text="Save") 27 | save.released.connect(self.save) 28 | button_layout.addWidget(save) 29 | cancel = QtGui.QPushButton(text="Cancel") 30 | cancel.released.connect(self.reject) 31 | button_layout.addWidget(cancel) 32 | 33 | self.layout.addLayout(button_layout) 34 | 35 | def save(self): 36 | self.racer.name = self.name.text() 37 | self.racer.hostname = self.hostname.text() 38 | self.racer.location = self.location.text() 39 | self.accept() 40 | 41 | -------------------------------------------------------------------------------- /time_trial_gui/gui/settings_tab.py: -------------------------------------------------------------------------------- 1 | from PyQt4 import QtGui 2 | from PyQt4 import QtCore 3 | from gui.racer_edit_dialog import RacerEditDialog 4 | from gui.sqlalchemy_table_model import SQLAlchemyTableModel 5 | from models.racer import Racer 6 | __author__ = 'daniel' 7 | 8 | 9 | class SettingsTab(QtGui.QWidget): 10 | 11 | def __init__(self, parent = None, session = None): 12 | super(SettingsTab, self).__init__(parent) 13 | self.session = session 14 | self.layout = QtGui.QGridLayout() 15 | self.setLayout(self.layout) 16 | 17 | racers_box = QtGui.QGroupBox("Racer Configuration") 18 | racers_box_layout = QtGui.QGridLayout() 19 | racers_box.setLayout(racers_box_layout) 20 | self.layout.addWidget(racers_box,0,0) 21 | 22 | self.racers_table = QtGui.QTableView(self) 23 | self.racers_table.doubleClicked.connect(self.edit_racer) 24 | self.racers_table.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) 25 | self.racers_table.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) 26 | self.racers_table_model = SQLAlchemyTableModel(session, Racer, [ 27 | ('name', Racer.name, 'name'), 28 | ('hostname', Racer.hostname, 'hostname'), 29 | ('location', Racer.location, 'location')]) 30 | 31 | 32 | self.racers_table_selection_model = QtGui.QItemSelectionModel(self.racers_table_model) 33 | self.racers_table.setModel(self.racers_table_selection_model.model()) 34 | self.racers_table.setSelectionModel(self.racers_table_selection_model) 35 | 36 | 37 | racers_box_layout.addWidget(self.racers_table,0,0, 1, 3) 38 | 39 | racers_add_button = QtGui.QPushButton("Add") 40 | racers_add_button.released.connect(self.add_racer) 41 | racers_box_layout.addWidget(racers_add_button, 1, 0) 42 | 43 | racers_edit_button = QtGui.QPushButton("Edit") 44 | racers_edit_button.released.connect(self.edit_racer) 45 | racers_box_layout.addWidget(racers_edit_button, 1, 1) 46 | 47 | 48 | racers_delete_button = QtGui.QPushButton("Delete") 49 | racers_delete_button.released.connect(self.delete_racer) 50 | racers_box_layout.addWidget(racers_delete_button, 1, 2) 51 | 52 | def edit_racer(self): 53 | racer = self.racers_table_selection_model.currentIndex().data(QtCore.Qt.EditRole) 54 | dialog = RacerEditDialog(racer) 55 | dialog.exec() 56 | self.session.add(racer) 57 | self.session.commit() 58 | self.racers_table_model.refresh() 59 | 60 | 61 | def add_racer(self): 62 | racer = Racer() 63 | dialog = RacerEditDialog(racer) 64 | dialog.exec() 65 | self.session.add(racer) 66 | self.session.commit() 67 | self.racers_table_model.refresh() 68 | 69 | def delete_racer(self): 70 | self.session.delete(self.racers_table_selection_model.currentIndex().data(QtCore.Qt.EditRole)) 71 | self.session.commit() 72 | self.racers_table_model.refresh() 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /time_trial_gui/gui/sqlalchemy_table_model.py: -------------------------------------------------------------------------------- 1 | from PyQt4.QtCore import QVariant 2 | 3 | __author__ = 'daniel' 4 | 5 | from PyQt4 import QtGui, QtCore, Qt 6 | 7 | ## adapted from https://gist.github.com/harvimt/4699169 by Mark Harviston 8 | 9 | class SQLAlchemyTableModel(QtCore.QAbstractTableModel): 10 | 11 | def __init__(self, session, query, columns, parent = None): 12 | QtCore.QAbstractTableModel.__init__(self, parent) 13 | self.session = session 14 | self.query = session.query(query) 15 | self.columns = columns 16 | self.results = None 17 | self.count = None 18 | self.sort = None 19 | self.filter = None 20 | self.refresh() 21 | 22 | def rowCount(self, parent): 23 | return self.count 24 | 25 | def refresh(self): 26 | self.layoutAboutToBeChanged.emit() 27 | 28 | q = self.query 29 | if self.sort is not None: 30 | order, col = self.sort 31 | col = self.columns[col][1] 32 | if order == Qt.DescendingOrder: 33 | col = col.desc() 34 | else: 35 | col = None 36 | 37 | if self.filter is not None: 38 | q = q.filter(self.filter) 39 | 40 | q = q.order_by(col) 41 | 42 | self.results = q.all() 43 | self.count = q.count() 44 | self.layoutChanged.emit() 45 | 46 | def columnCount(self, parent): 47 | return len(self.columns) 48 | 49 | def headerData(self, col, Qt_Orientation, int_role=None): 50 | if int_role == QtCore.Qt.DisplayRole and Qt_Orientation == QtCore.Qt.Horizontal: 51 | return self.columns[col][0] 52 | return QtCore.QAbstractTableModel.headerData(self, col, Qt_Orientation, int_role) 53 | 54 | 55 | def data(self, index, role): 56 | if not index.isValid(): 57 | return None 58 | if role == QtCore.Qt.EditRole: 59 | return self.results[index.row()] 60 | elif role != QtCore.Qt.DisplayRole: 61 | return None 62 | else: 63 | row = self.results[index.row()] 64 | name = self.columns[index.column()][2] 65 | return str(getattr(row, name)) 66 | 67 | def sort(self, col, order): 68 | self.sort = order, col 69 | self.refresh() 70 | 71 | def setFilter(self, filter): 72 | self.filter = filter 73 | self.refresh() 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /time_trial_gui/gui/trial_detail_widget.py: -------------------------------------------------------------------------------- 1 | __author__ = 'daniel' 2 | 3 | from PyQt4 import QtGui, QtCore 4 | 5 | from PyQt4.QtCore import pyqtSignal 6 | 7 | class TrialDetailsWidget(QtGui.QWidget): 8 | def __init__(self, parent=None): 9 | super(TrialDetailsWidget, self).__init__(parent) 10 | 11 | self.layout = QtGui.QVBoxLayout() 12 | self.setLayout(self.layout) 13 | 14 | self.box = QtGui.QGroupBox("Trial Settings") 15 | self.layout.addWidget(self.box) 16 | 17 | self.box_layout = QtGui.QFormLayout() 18 | self.box_layout.setFormAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft) 19 | self.box.setLayout(self.box_layout) 20 | 21 | 22 | self.type = QtGui.QLabel("") 23 | self.box_layout.addRow("Type", self.type) 24 | 25 | self.name = QtGui.QLabel("") 26 | self.box_layout.addRow("Name", self.name) 27 | 28 | self.description = QtGui.QLabel("") 29 | self.box_layout.addRow("Description", self.description) 30 | 31 | 32 | class EchoTrialDetailsWidget(TrialDetailsWidget): 33 | 34 | def __init__(self, parent=None): 35 | super(EchoTrialDetailsWidget, self).__init__(parent) 36 | 37 | self.delay = QtGui.QLabel("") 38 | self.box_layout.addRow("Delay (ns)", self.delay) 39 | 40 | class HttpTrialDetailsWidget(TrialDetailsWidget): 41 | 42 | def __init__(self, parent=None): 43 | super(HttpTrialDetailsWidget, self).__init__(parent) 44 | 45 | self.request_url = QtGui.QLabel("") 46 | self.box_layout.addRow("Request URL", self.request_url) 47 | 48 | 49 | class RacerDetailsWidget(QtGui.QWidget): 50 | 51 | def __init__(self, parent=None): 52 | super(RacerDetailsWidget, self).__init__(parent) 53 | 54 | self.layout = QtGui.QVBoxLayout() 55 | self.setLayout(self.layout) 56 | 57 | 58 | self.box = QtGui.QGroupBox("Racer Settings") 59 | self.layout.addWidget(self.box) 60 | self.box_layout = QtGui.QFormLayout() 61 | self.box_layout.setFormAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft) 62 | self.box.setLayout(self.box_layout) 63 | 64 | 65 | self.racer = QtGui.QLabel("") 66 | self.box_layout.addRow("Racer", self.racer) 67 | 68 | 69 | self.core_id = QtGui.QLabel("") 70 | self.box_layout.addRow("Core ID", self.core_id) 71 | 72 | self.real_time = QtGui.QLabel("") 73 | self.box_layout.addRow("Real-Time", self.real_time) 74 | 75 | 76 | 77 | 78 | class TrialStatusWidget(QtGui.QWidget): 79 | 80 | trial_started = pyqtSignal() 81 | trial_stopped = pyqtSignal() 82 | trial_refreshed = pyqtSignal() 83 | trial_edit = pyqtSignal() 84 | 85 | def __init__(self, parent=None): 86 | super(TrialStatusWidget, self).__init__(parent) 87 | 88 | self.layout = QtGui.QVBoxLayout() 89 | self.setLayout(self.layout) 90 | 91 | self.box = QtGui.QGroupBox("Trial Status") 92 | self.super_box_layout = QtGui.QGridLayout() 93 | 94 | self.box_layout = QtGui.QFormLayout() 95 | self.box_layout.setFormAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft) 96 | self.box.setLayout(self.super_box_layout) 97 | self.super_box_layout.addLayout(self.box_layout,0,0,1,2) 98 | 99 | 100 | self.layout.addWidget(self.box) 101 | 102 | self.start = QtGui.QLabel("") 103 | self.box_layout.addRow("Start", self.start) 104 | 105 | 106 | self.end = QtGui.QLabel("") 107 | self.box_layout.addRow("End", self.end) 108 | 109 | self.job_status = QtGui.QLabel("") 110 | self.box_layout.addRow("Job Status", self.job_status) 111 | 112 | 113 | 114 | self.start_trial_button = QtGui.QPushButton("Start") 115 | self.start_trial_button.setEnabled(False) 116 | self.start_trial_button.released.connect(self.trial_started.emit) 117 | self.super_box_layout.addWidget(self.start_trial_button,1,0) 118 | 119 | 120 | self.stop_trial_button = QtGui.QPushButton("Cancel and Reset") 121 | self.stop_trial_button.setEnabled(False) 122 | self.stop_trial_button.released.connect(self.trial_stopped.emit) 123 | self.super_box_layout.addWidget(self.stop_trial_button,1,1) 124 | 125 | self.refresh_trial_button = QtGui.QPushButton("Refresh") 126 | self.refresh_trial_button.setEnabled(False) 127 | self.refresh_trial_button.released.connect(self.trial_refreshed.emit) 128 | self.layout.addWidget(self.refresh_trial_button) 129 | 130 | 131 | self.edit_trial_button = QtGui.QPushButton("Edit") 132 | self.edit_trial_button.setEnabled(False) 133 | self.edit_trial_button.released.connect(self.trial_edit.emit) 134 | self.layout.addWidget(self.edit_trial_button) 135 | 136 | 137 | -------------------------------------------------------------------------------- /time_trial_gui/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmayer/time_trial/c9f896f8788bb4ae3b8bd526a325542c610d0573/time_trial_gui/images/.DS_Store -------------------------------------------------------------------------------- /time_trial_gui/images/clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmayer/time_trial/c9f896f8788bb4ae3b8bd526a325542c610d0573/time_trial_gui/images/clock.png -------------------------------------------------------------------------------- /time_trial_gui/lib/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'daniel' 2 | -------------------------------------------------------------------------------- /time_trial_gui/lib/base.py: -------------------------------------------------------------------------------- 1 | __author__ = 'daniel' 2 | from sqlalchemy.ext.declarative import declarative_base 3 | Base = declarative_base() 4 | -------------------------------------------------------------------------------- /time_trial_gui/lib/box_test.py: -------------------------------------------------------------------------------- 1 | __author__ = 'daniel' 2 | 3 | import numpy 4 | 5 | 6 | 7 | class BoxTest: 8 | def __init__(self, data_x, data_y, i, j): 9 | self.data_x = data_x 10 | self.data_y = data_y 11 | self.i = i 12 | self.j = j 13 | 14 | 15 | def perform(self): 16 | # compute quantiles. Not using ranges since they are only overhead. 17 | self.x_q_i = self.data_x.quantile(self.i) 18 | self.x_q_j = self.data_x.quantile(self.j) 19 | self.y_q_i = self.data_y.quantile(self.i) 20 | self.y_q_j = self.data_y.quantile(self.j) 21 | print(self.x_q_i) 22 | print(self.x_q_j) 23 | print(self.y_q_i) 24 | print(self.y_q_j) 25 | 26 | overlap = self.overlap(self.x_q_i, self.x_q_j, self.y_q_i, self.y_q_j) 27 | 28 | return not overlap and self.x_q_j < self.y_q_i 29 | 30 | def x_box(self): 31 | return [self.x_q_i, self.x_q_j] 32 | 33 | def y_box(self): 34 | return [self.y_q_i, self.y_q_j] 35 | 36 | def overlap (self, a_lower, a_upper, b_lower, b_upper): 37 | return a_upper > b_lower and a_lower < b_upper 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /time_trial_gui/lib/filters.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | class PercentileFilter: 4 | 5 | def __init__(self): 6 | pass 7 | 8 | def apply(self, timing_data, percentile): 9 | np_data = np.array(timing_data.data) 10 | return np.percentile(np_data, percentile) -------------------------------------------------------------------------------- /time_trial_gui/lib/plot.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Plot: 4 | 5 | def __init__(self, timing_data, label = "Data"): 6 | self.timing_data = timing_data 7 | self.label = label 8 | self.color = "red" 9 | self.bins = 25 10 | self.style = "stepfilled" 11 | self.style_types = {'bar' : "Bar", 12 | 'step' : "Step", 13 | 'stepfilled' : "Step - Filled"} 14 | self.minimum = 0 15 | self.maximum = 99 16 | self.range_type = "percentile" 17 | 18 | 19 | def style_name(self): 20 | return self.style_types[self.style] 21 | -------------------------------------------------------------------------------- /time_trial_gui/lib/plot_settings.py: -------------------------------------------------------------------------------- 1 | 2 | class PlotSettings: 3 | 4 | def __init__(self): 5 | self.x_axis_label = 'Time / [ns]' 6 | self.y_axis_label = 'Frequency' 7 | self.legend = False 8 | 9 | self.x_minimum = None 10 | self.x_maximum = None 11 | self.y_minimum = None 12 | self.y_maximum = None 13 | 14 | self.x_scaling = 1 15 | 16 | 17 | pass 18 | -------------------------------------------------------------------------------- /time_trial_gui/lib/racer_driver.py: -------------------------------------------------------------------------------- 1 | from http.server import BaseHTTPRequestHandler 2 | from io import StringIO, BytesIO 3 | import os 4 | 5 | __author__ = 'daniel' 6 | 7 | CPP_HTTP_TIMING_EXECUTABLE = '../racer/bin/run_http_timing_client' 8 | CPP_ECHO_TIMING_EXECUTABLE = "../racer/bin/run_timing_client" 9 | 10 | 11 | import subprocess 12 | 13 | 14 | class ParseException(Exception): 15 | pass 16 | 17 | class HTTPRequest(BaseHTTPRequestHandler): 18 | def __init__(self, request_text): 19 | self.rfile = BytesIO(request_text) 20 | self.raw_requestline = self.rfile.readline() 21 | self.error_code = self.error_message = None 22 | self.parse_request() 23 | self.request_body = self.rfile.read() 24 | 25 | def send_error(self, code, message): 26 | self.error_code = code 27 | self.error_message = message 28 | 29 | def parse_request(request_text): 30 | request = HTTPRequest(request_text) 31 | 32 | if request.error_code: 33 | raise ParseException(request.error_message) 34 | 35 | # request.headers is still a message object 36 | # items returns a list of tuples, but we need actual header k,v pairs: 37 | header_list = [x[0] + ": " + x[1] for x in request.headers.items()] 38 | 39 | return (request.command, request.path, request.request_version, request.request_body, header_list ) 40 | 41 | 42 | 43 | 44 | def execute_trial(trial): 45 | cmd = [] 46 | 47 | if trial.__class__.__name__ == "HTTPTrialJob": 48 | print("Executing HTTP Trial...") 49 | print(repr(trial.request)) 50 | 51 | try: 52 | verb, path, version, body, headers = parse_request(bytes(trial.request,"iso-8859-1")) 53 | 54 | except ParseException as e: 55 | print("unable to parse request: %s" % e) 56 | 57 | cmd = [CPP_HTTP_TIMING_EXECUTABLE, trial.request_url + path, verb, version, body, str(trial.real_time), str(trial.core_affinity), " ", str(trial.reps)] 58 | cmd.extend(headers) 59 | print("running %s, args %s" % (CPP_HTTP_TIMING_EXECUTABLE, cmd)) 60 | 61 | else: 62 | print("Executing Echo Trial...") 63 | #TODO: get this from a config file 64 | cmd.append(CPP_ECHO_TIMING_EXECUTABLE) 65 | cmd.append(trial.target_host) 66 | cmd.append(str(trial.target_port)) 67 | cmd.append(str(int(trial.real_time))) 68 | cmd.append(str(trial.core_affinity)) 69 | cmd.append(str(trial.delay)) 70 | cmd.append(str(trial.reps)) 71 | print(cmd) 72 | 73 | return subprocess.check_output(cmd) 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /time_trial_gui/lib/rq_result_processor.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from time import sleep 3 | from rq.job import Job 4 | from models.trial import Trial 5 | from redis import Redis 6 | 7 | __author__ = 'daniel' 8 | 9 | import threading 10 | 11 | class RqResultsProcessor(threading.Thread): 12 | session = None 13 | stopped = False 14 | 15 | def stop(self): 16 | self.stopped = True 17 | 18 | def run(self): 19 | redis_conn = Redis() 20 | # get all 21 | while True: 22 | incomplete = self.session.query(Trial).filter(Trial.end_date == None).filter(Trial.start_date!=None).all() 23 | for t in incomplete: 24 | try: 25 | job = Job.fetch(t.job, connection=redis_conn) 26 | except: 27 | print("Exception occurred. Moving on.") 28 | sleep(1) 29 | continue 30 | 31 | if job.result is not None: 32 | print("Result for " + t.name + " found.") 33 | t.result = job.result 34 | t.end_date = datetime.now() 35 | 36 | self.session.add(t) 37 | self.session.commit() 38 | self.session.expire(t) 39 | 40 | if self.stopped: 41 | self.session.close() 42 | return 43 | 44 | sleep(1) 45 | -------------------------------------------------------------------------------- /time_trial_gui/lib/timing_data.py: -------------------------------------------------------------------------------- 1 | import re 2 | import os.path 3 | import logging 4 | import lib.filters as filters 5 | 6 | class TimingData: 7 | 8 | def __init__(self): 9 | self.data = [] 10 | self.logger = logging.getLogger('time_trial') 11 | 12 | 13 | def load_from_csv(self, file): 14 | self.full_path = file 15 | self.file_name = os.path.basename(file) 16 | self.logger.info("Loading from " + file) 17 | 18 | 19 | data_file = open(file) 20 | self.parse_csv(data_file) 21 | 22 | def parse_csv(self, data): 23 | if data == None or data == "": 24 | return 25 | 26 | 27 | if isinstance(data, str): 28 | str_data = data.split('\n') 29 | else: 30 | str_data = data.decode(encoding="utf-8").split('\n') 31 | 32 | 33 | for f in str_data: 34 | if ";" in str(f): 35 | s = f.split(";")[0] 36 | ns = f.split(";")[1] 37 | if ns is not None and ns != "": 38 | ns = int(re.sub(r"\D","", ns)) 39 | s = int(re.sub(r"\D","", s)) 40 | self.data.append(s*1e9 + ns) 41 | 42 | 43 | def quantile(self, i): 44 | return filters.PercentileFilter().apply(self, i) 45 | 46 | -------------------------------------------------------------------------------- /time_trial_gui/lib/trial_jobs.py: -------------------------------------------------------------------------------- 1 | __author__ = 'daniel' 2 | 3 | class TrialJob: 4 | def __init__(self): 5 | self.real_time = 1 6 | self.core_affinity = 1 7 | self.reps = 1 8 | 9 | 10 | 11 | class EchoTrialJob(TrialJob): 12 | def __init__(self): 13 | TrialJob.__init__(self) 14 | # in nanoseconds (for now :-/) 15 | self.delay = 100 16 | self.target_host = "" 17 | self.target_port = "" 18 | 19 | 20 | class HTTPTrialJob(TrialJob): 21 | def __init__(self): 22 | TrialJob.__init__(self) 23 | self.request_url = "" 24 | self.request = "" 25 | -------------------------------------------------------------------------------- /time_trial_gui/models/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'daniel' 2 | -------------------------------------------------------------------------------- /time_trial_gui/models/experiment.py: -------------------------------------------------------------------------------- 1 | __author__ = 'daniel' 2 | 3 | from sqlalchemy import Integer, Column, String 4 | from sqlalchemy.orm import relationship 5 | from lib.base import Base 6 | 7 | 8 | class Experiment(Base): 9 | __tablename__ = "experiments" 10 | id = Column(Integer, primary_key=True) 11 | name = Column(String(50)) 12 | trials = relationship("Trial", 13 | primaryjoin="Trial.experiment_id==Experiment.id") 14 | -------------------------------------------------------------------------------- /time_trial_gui/models/racer.py: -------------------------------------------------------------------------------- 1 | __author__ = 'daniel' 2 | 3 | from sqlalchemy import Integer, Column, String 4 | from sqlalchemy.orm import relationship 5 | from lib.base import Base 6 | 7 | 8 | class Racer(Base): 9 | __tablename__ = "racers" 10 | id = Column(Integer, primary_key=True) 11 | name = Column(String(50)) 12 | hostname = Column(String(50)) 13 | location = Column(String(50)) 14 | trials = relationship("Trial", primaryjoin="Racer.id==Trial.racer_id") 15 | -------------------------------------------------------------------------------- /time_trial_gui/models/trial.py: -------------------------------------------------------------------------------- 1 | __author__ = 'daniel' 2 | 3 | from sqlalchemy import Integer, Column, String, ForeignKey, Text, DateTime,Boolean 4 | from sqlalchemy.orm import relationship, deferred 5 | from lib.base import Base 6 | from models.racer import Racer 7 | 8 | from PyQt4 import QtCore 9 | 10 | 11 | class Trial(Base): 12 | __tablename__ = "trials" 13 | 14 | discriminator = Column('type', String(50)) 15 | __mapper_args__ = {'polymorphic_on': discriminator} 16 | 17 | id = Column(Integer, primary_key=True) 18 | 19 | name = Column(String(50)) 20 | description = Column(String(1000)) 21 | 22 | core_id = Column(Integer) 23 | real_time = Column(Boolean) 24 | reps = Column(Integer) 25 | 26 | status = Column(String(50)) 27 | job = Column(Text) 28 | result = deferred(Column(Text)) 29 | start_date = Column(DateTime) 30 | end_date = Column(DateTime) 31 | 32 | racer_id = Column(Integer, ForeignKey('racers.id')) 33 | racer = relationship("Racer", primaryjoin="Trial.racer_id==Racer.id") 34 | 35 | experiment_id = Column(Integer, ForeignKey('experiments.id')) 36 | experiment = relationship("Experiment", primaryjoin="Trial.experiment_id==Experiment.id") 37 | 38 | class EchoTrial(Trial): 39 | __tablename__ = 'echo_trials' 40 | __mapper_args__ = {'polymorphic_identity': 'Echo Trial'} 41 | id = Column(Integer, ForeignKey('trials.id'), primary_key=True) 42 | host = Column(String(100)) 43 | port = Column(Integer) 44 | delay = Column(Integer) 45 | 46 | def duplicate(self): 47 | x = EchoTrial() 48 | 49 | #common 50 | x.name = self.name 51 | x.description = self.description 52 | x.core_id = self.core_id 53 | x.real_time = self.real_time 54 | x.reps = self.reps 55 | x.racer = self.racer 56 | x.experiment = self.experiment 57 | 58 | 59 | x.host = self.host 60 | x.port = self.port 61 | x.delay = self.delay 62 | 63 | return x 64 | 65 | 66 | class HTTPTrial(Trial): 67 | __tablename__ = 'http_trials' 68 | __mapper_args__ = {'polymorphic_identity': 'HTTP Trial'} 69 | id = Column(Integer, ForeignKey('trials.id'), primary_key=True) 70 | request_url = Column(String(500)) 71 | request = Column(String(50000)) 72 | 73 | def duplicate(self): 74 | x = HTTPTrial() 75 | 76 | #common 77 | x.name = self.name 78 | x.description = self.description 79 | x.core_id = self.core_id 80 | x.real_time = self.real_time 81 | x.reps = self.reps 82 | x.racer = self.racer 83 | x.experiment = self.experiment 84 | 85 | x.request_url = self.request_url 86 | x.request = self.request 87 | 88 | return x 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /time_trial_gui/requirements.txt: -------------------------------------------------------------------------------- 1 | rq 2 | matplotlib 3 | numpy 4 | rq_dashboard 5 | sqlalchemy 6 | pygments -------------------------------------------------------------------------------- /time_trial_gui/time_trial.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from PyQt4 import QtGui, QtCore 4 | import sip 5 | from sqlalchemy.orm import sessionmaker 6 | from gui.experiments_tab import ExperimentsTab 7 | 8 | from gui.feasibility_tab import FeasibilityTab 9 | from gui.plotter_tab import PlotterTab 10 | 11 | import logging 12 | from sqlalchemy import create_engine 13 | from gui.settings_tab import SettingsTab 14 | from lib.rq_result_processor import RqResultsProcessor 15 | from models.racer import Racer 16 | from models.trial import Trial 17 | from models.experiment import Experiment 18 | from lib.base import Base 19 | 20 | 21 | 22 | 23 | progname = "Time Trial" 24 | progversion = "0.1" 25 | 26 | 27 | class ApplicationWindow(QtGui.QMainWindow): 28 | def __init__(self): 29 | ################ 30 | # Data Init 31 | ################ 32 | logger = logging.getLogger('time_trial') 33 | logger.setLevel(logging.DEBUG) 34 | ch = logging.StreamHandler() 35 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 36 | ch.setFormatter(formatter) 37 | logger.addHandler(ch) 38 | 39 | logger.info("Time Trial starting up...") 40 | 41 | ##################### 42 | # Connect Database 43 | ##################### 44 | self.engine = create_engine('sqlite:///data.sqlite', connect_args={'check_same_thread':False}, echo=False) 45 | Base.metadata.create_all(self.engine, checkfirst=True) 46 | Session = sessionmaker(bind=self.engine) 47 | self.session = Session() 48 | 49 | ################################ 50 | # Launching Background Threads 51 | ################################ 52 | 53 | # thread to check for results 54 | self.processor = RqResultsProcessor() 55 | ThreadSession = sessionmaker(bind=self.engine) 56 | self.processor.session = ThreadSession() 57 | self.processor.start() 58 | 59 | 60 | 61 | 62 | ################ 63 | # G U I 64 | ################ 65 | QtGui.QMainWindow.__init__(self) 66 | self.setAttribute(QtCore.Qt.WA_DeleteOnClose) 67 | self.setWindowTitle("Time Trial") 68 | self.setWindowIcon(QtGui.QIcon('images/clock.png')) 69 | 70 | 71 | ################ 72 | # M E N U 73 | ################ 74 | 75 | 76 | self.file_menu = QtGui.QMenu('&File', self) 77 | self.file_menu.addAction('&Quit', self.fileQuit, 78 | QtCore.Qt.CTRL + QtCore.Qt.Key_Q) 79 | self.menuBar().addMenu(self.file_menu) 80 | 81 | self.help_menu = QtGui.QMenu('&Help', self) 82 | self.menuBar().addSeparator() 83 | self.menuBar().addMenu(self.help_menu) 84 | 85 | self.help_menu.addAction('&About', self.about) 86 | 87 | ################ 88 | # Main Window 89 | ################ 90 | self.main_widget = QtGui.QWidget(self) 91 | self.main_layout = QtGui.QGridLayout(self.main_widget) 92 | 93 | 94 | 95 | self.tab_widget = QtGui.QTabWidget(self.main_widget) 96 | self.main_layout.addWidget(self.tab_widget,0,0) 97 | 98 | self.experiment_tab = ExperimentsTab(session = self.session, parent = self.main_widget) 99 | 100 | QtCore.QObject.connect(self.experiment_tab, 101 | QtCore.SIGNAL("shorter_trial_set(PyQt_PyObject)") , 102 | self.shorter_trial_set) 103 | 104 | 105 | QtCore.QObject.connect(self.experiment_tab, 106 | QtCore.SIGNAL("longer_trial_set(PyQt_PyObject)") , 107 | self.longer_trial_set) 108 | 109 | self.tab_widget.addTab(self.experiment_tab, "Experiments") 110 | 111 | self.feasibility_tab = FeasibilityTab(session = self.session, parent = self.main_widget) 112 | self.tab_widget.addTab(self.feasibility_tab, "Feasibility Analysis") 113 | # 114 | # self.plotter_tab = PlotterTab(self.main_widget) 115 | # self.tab_widget.addTab(self.plotter_tab, "Plotter") 116 | # 117 | self.settings_tab = SettingsTab(self.main_widget, session = self.session) 118 | self.tab_widget.addTab(self.settings_tab, "Settings") 119 | 120 | self.main_widget.setFocus() 121 | self.setCentralWidget(self.main_widget) 122 | 123 | def shorter_trial_set(self, trial): 124 | self.feasibility_tab.set_shorter(trial) 125 | 126 | def longer_trial_set(self, trial): 127 | self.feasibility_tab.set_longer(trial) 128 | 129 | 130 | def fileQuit(self): 131 | self.close() 132 | 133 | def closeEvent(self, ce): 134 | self.session.close() 135 | self.processor.stop() 136 | self.processor.join() 137 | self.fileQuit() 138 | 139 | def about(self): 140 | QtGui.QMessageBox.about(self, "About", """TKTK""" ) 141 | 142 | 143 | 144 | 145 | 146 | 147 | # The following call prevents C++ destructors from being called. 148 | # The order of calling would be arbitrary and causes a crash when 149 | # closing the PyQt app. 150 | # http://pyqt.sourceforge.net/Docs/sip4/python_api.html 151 | # https://stackoverflow.com/questions/23565702/pyqt4-crashed-on-exit 152 | sip.setdestroyonexit(False) 153 | 154 | qApp = QtGui.QApplication(sys.argv) 155 | qApp.setWindowIcon(QtGui.QIcon('images/clock.png')) 156 | qApp.setApplicationName("Time Trial") 157 | 158 | aw = ApplicationWindow() 159 | aw.setWindowTitle("%s" % progname) 160 | 161 | desktop = QtGui.QDesktopWidget() 162 | screen_size = desktop.availableGeometry() 163 | width = min(screen_size.width() * 0.9, 1024) 164 | height = min(screen_size.height() * 0.95, 900) 165 | 166 | aw.setFixedSize(QtCore.QSize(width, height)) 167 | aw.show() 168 | aw.raise_() 169 | 170 | 171 | sys.exit(qApp.exec_()) 172 | #qApp.exec_() 173 | 174 | --------------------------------------------------------------------------------