├── Original_doc.md ├── README.md ├── WRITEUP.md ├── fac.ffm ├── ffmpeg └── server.conf ├── images ├── arch_diagram.png ├── jupy1.png ├── jupy2.png └── people-counter-image.png ├── inference.py ├── linux-setup.md ├── mac-setup.md ├── main.py ├── resources ├── .DS_Store └── Pedestrian_Detect_2_1_1.mp4 ├── webservice ├── .DS_Store ├── server │ ├── .DS_Store │ ├── .gitignore │ ├── README.md │ ├── node-server │ │ ├── .DS_Store │ │ ├── config.js │ │ ├── mqtt.js │ │ └── server.js │ ├── package-lock.json │ └── package.json └── ui │ ├── .babelrc │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── MqttClient.js │ ├── assets │ │ ├── fonts │ │ │ ├── FontAwesome.otf │ │ │ ├── IntelClear-Bold.eot │ │ │ ├── IntelClear-Bold.ttf │ │ │ ├── IntelClear-Bold.woff │ │ │ ├── IntelClear-BoldItalic.eot │ │ │ ├── IntelClear-BoldItalic.ttf │ │ │ ├── IntelClear-BoldItalic.woff │ │ │ ├── IntelClear-Italic.eot │ │ │ ├── IntelClear-Italic.ttf │ │ │ ├── IntelClear-Italic.woff │ │ │ ├── IntelClear-Light.eot │ │ │ ├── IntelClear-Light.ttf │ │ │ ├── IntelClear-Light.woff │ │ │ ├── IntelClear-LightItalic.eot │ │ │ ├── IntelClear-LightItalic.ttf │ │ │ ├── IntelClear-LightItalic.woff │ │ │ ├── IntelClear-Regular.eot │ │ │ ├── IntelClear-Regular.ttf │ │ │ ├── IntelClear-Regular.woff │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ └── fontawesome-webfont.woff2 │ │ └── images │ │ │ ├── Group.svg │ │ │ ├── background-02.jpg │ │ │ └── intel-people-counter.svg │ ├── components │ │ ├── button │ │ │ ├── Button.css │ │ │ ├── Button.jsx │ │ │ └── Button.test.jsx │ │ ├── data-box │ │ │ ├── DataBox.css │ │ │ └── DataBox.jsx │ │ ├── graph-pane │ │ │ ├── GraphPane.css │ │ │ └── GraphPane.jsx │ │ └── navigation │ │ │ ├── ConnectedNavigation.jsx │ │ │ ├── Navigation.css │ │ │ └── Navigation.jsx │ ├── constants │ │ ├── constants.css │ │ └── constants.js │ ├── dux │ │ ├── reducers.js │ │ └── stats.js │ ├── features │ │ ├── camera-feed │ │ │ ├── CameraFeed.css │ │ │ └── CameraFeed.jsx │ │ └── stats │ │ │ ├── ConnectedStats.jsx │ │ │ ├── Stats.css │ │ │ └── Stats.jsx │ ├── font-awesome.css │ ├── index.css │ ├── index.html │ ├── index.jsx │ └── pages │ │ └── monitor │ │ └── Monitor.jsx │ ├── webpack.config.js │ ├── webpack.dev.config.js │ └── webpack.dist.config.js └── windows-setup.md /Original_doc.md: -------------------------------------------------------------------------------- 1 | # Deploy a People Counter App at the Edge 2 | 3 | | Details | | 4 | |-----------------------|---------------| 5 | | Programming Language: | Python 3.5 or 3.6 | 6 | 7 | ![people-counter-python](./images/people-counter-image.png) 8 | 9 | ## What it Does 10 | 11 | The people counter application will demonstrate how to create a smart video IoT solution using Intel® hardware and software tools. The app will detect people in a designated area, providing the number of people in the frame, average duration of people in frame, and total count. 12 | 13 | ## How it Works 14 | 15 | The counter will use the Inference Engine included in the Intel® Distribution of OpenVINO™ Toolkit. The model used should be able to identify people in a video frame. The app should count the number of people in the current frame, the duration that a person is in the frame (time elapsed between entering and exiting a frame) and the total count of people. It then sends the data to a local web server using the Paho MQTT Python package. 16 | 17 | You will choose a model to use and convert it with the Model Optimizer. 18 | 19 | ![architectural diagram](./images/arch_diagram.png) 20 | 21 | ## Requirements 22 | 23 | ### Hardware 24 | 25 | * 6th to 10th generation Intel® Core™ processor with Iris® Pro graphics or Intel® HD Graphics. 26 | * OR use of Intel® Neural Compute Stick 2 (NCS2) 27 | * OR Udacity classroom workspace for the related course 28 | 29 | ### Software 30 | 31 | * Intel® Distribution of OpenVINO™ toolkit 2019 R3 release 32 | * Node v6.17.1 33 | * Npm v3.10.10 34 | * CMake 35 | * MQTT Mosca server 36 | 37 | 38 | ## Setup 39 | 40 | ### Install Intel® Distribution of OpenVINO™ toolkit 41 | 42 | Utilize the classroom workspace, or refer to the relevant instructions for your operating system for this step. 43 | 44 | - [Linux/Ubuntu](./linux-setup.md) 45 | - [Mac](./mac-setup.md) 46 | - [Windows](./windows-setup.md) 47 | 48 | ### Install Nodejs and its dependencies 49 | 50 | Utilize the classroom workspace, or refer to the relevant instructions for your operating system for this step. 51 | 52 | - [Linux/Ubuntu](./linux-setup.md) 53 | - [Mac](./mac-setup.md) 54 | - [Windows](./windows-setup.md) 55 | 56 | ### Install npm 57 | 58 | There are three components that need to be running in separate terminals for this application to work: 59 | 60 | - MQTT Mosca server 61 | - Node.js* Web server 62 | - FFmpeg server 63 | 64 | From the main directory: 65 | 66 | * For MQTT/Mosca server: 67 | ``` 68 | cd webservice/server 69 | npm install 70 | ``` 71 | 72 | * For Web server: 73 | ``` 74 | cd ../ui 75 | npm install 76 | ``` 77 | **Note:** If any configuration errors occur in mosca server or Web server while using **npm install**, use the below commands: 78 | ``` 79 | sudo npm install npm -g 80 | rm -rf node_modules 81 | npm cache clean 82 | npm config set registry "http://registry.npmjs.org" 83 | npm install 84 | ``` 85 | 86 | ## What model to use 87 | 88 | It is up to you to decide on what model to use for the application. You need to find a model not already converted to Intermediate Representation format (i.e. not one of the Intel® Pre-Trained Models), convert it, and utilize the converted model in your application. 89 | 90 | Note that you may need to do additional processing of the output to handle incorrect detections, such as adjusting confidence threshold or accounting for 1-2 frames where the model fails to see a person already counted and would otherwise double count. 91 | 92 | **If you are otherwise unable to find a suitable model after attempting and successfully converting at least three other models**, you can document in your write-up what the models were, how you converted them, and why they failed, and then utilize any of the Intel® Pre-Trained Models that may perform better. 93 | 94 | ## Run the application 95 | 96 | From the main directory: 97 | 98 | ### Step 1 - Start the Mosca server 99 | 100 | ``` 101 | cd webservice/server/node-server 102 | node ./server.js 103 | ``` 104 | 105 | You should see the following message, if successful: 106 | ``` 107 | Mosca server started. 108 | ``` 109 | 110 | ### Step 2 - Start the GUI 111 | 112 | Open new terminal and run below commands. 113 | ``` 114 | cd webservice/ui 115 | npm run dev 116 | ``` 117 | 118 | You should see the following message in the terminal. 119 | ``` 120 | webpack: Compiled successfully 121 | ``` 122 | 123 | ### Step 3 - FFmpeg Server 124 | 125 | Open new terminal and run the below commands. 126 | ``` 127 | sudo ffserver -f ./ffmpeg/server.conf 128 | ``` 129 | 130 | ### Step 4 - Run the code 131 | 132 | Open a new terminal to run the code. 133 | 134 | #### Setup the environment 135 | 136 | You must configure the environment to use the Intel® Distribution of OpenVINO™ toolkit one time per session by running the following command: 137 | ``` 138 | source /opt/intel/openvino/bin/setupvars.sh -pyver 3.5 139 | ``` 140 | 141 | You should also be able to run the application with Python 3.6, although newer versions of Python will not work with the app. 142 | 143 | #### Running on the CPU 144 | 145 | When running Intel® Distribution of OpenVINO™ toolkit Python applications on the CPU, the CPU extension library is required. This can be found at: 146 | 147 | ``` 148 | /opt/intel/openvino/deployment_tools/inference_engine/lib/intel64/ 149 | ``` 150 | 151 | *Depending on whether you are using Linux or Mac, the filename will be either `libcpu_extension_sse4.so` or `libcpu_extension.dylib`, respectively.* (The Linux filename may be different if you are using a AVX architecture) 152 | 153 | Though by default application runs on CPU, this can also be explicitly specified by ```-d CPU``` command-line argument: 154 | 155 | ``` 156 | python main.py -i resources/Pedestrian_Detect_2_1_1.mp4 -m your-model.xml -l /opt/intel/openvino/deployment_tools/inference_engine/lib/intel64/libcpu_extension_sse4.so -d CPU -pt 0.6 | ffmpeg -v warning -f rawvideo -pixel_format bgr24 -video_size 768x432 -framerate 24 -i - http://0.0.0.0:3004/fac.ffm 157 | ``` 158 | If you are in the classroom workspace, use the “Open App” button to view the output. If working locally, to see the output on a web based interface, open the link [http://0.0.0.0:3004](http://0.0.0.0:3004/) in a browser. 159 | 160 | #### Running on the Intel® Neural Compute Stick 161 | 162 | To run on the Intel® Neural Compute Stick, use the ```-d MYRIAD``` command-line argument: 163 | 164 | ``` 165 | python3.5 main.py -d MYRIAD -i resources/Pedestrian_Detect_2_1_1.mp4 -m your-model.xml -pt 0.6 | ffmpeg -v warning -f rawvideo -pixel_format bgr24 -video_size 768x432 -framerate 24 -i - http://0.0.0.0:3004/fac.ffm 166 | ``` 167 | 168 | To see the output on a web based interface, open the link [http://0.0.0.0:3004](http://0.0.0.0:3004/) in a browser. 169 | 170 | **Note:** The Intel® Neural Compute Stick can only run FP16 models at this time. The model that is passed to the application, through the `-m ` command-line argument, must be of data type FP16. 171 | 172 | #### Using a camera stream instead of a video file 173 | 174 | To get the input video from the camera, use the `-i CAM` command-line argument. Specify the resolution of the camera using the `-video_size` command line argument. 175 | 176 | For example: 177 | ``` 178 | python main.py -i CAM -m your-model.xml -l /opt/intel/openvino/deployment_tools/inference_engine/lib/intel64/libcpu_extension_sse4.so -d CPU -pt 0.6 | ffmpeg -v warning -f rawvideo -pixel_format bgr24 -video_size 768x432 -framerate 24 -i - http://0.0.0.0:3004/fac.ffm 179 | ``` 180 | 181 | To see the output on a web based interface, open the link [http://0.0.0.0:3004](http://0.0.0.0:3004/) in a browser. 182 | 183 | **Note:** 184 | User has to give `-video_size` command line argument according to the input as it is used to specify the resolution of the video or image file. 185 | 186 | ## A Note on Running Locally 187 | 188 | The servers herein are configured to utilize the Udacity classroom workspace. As such, 189 | to run on your local machine, you will need to change the below file: 190 | 191 | ``` 192 | webservice/ui/src/constants/constants.js 193 | ``` 194 | 195 | The `CAMERA_FEED_SERVER` and `MQTT_SERVER` both use the workspace configuration. 196 | You can change each of these as follows: 197 | 198 | ``` 199 | CAMERA_FEED_SERVER: "http://localhost:3004" 200 | ... 201 | MQTT_SERVER: "ws://localhost:3002" 202 | ``` 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deploying a People Counter Application at the Edge 2 | 3 | The people counter application demonstrates how to create a smart video IoT solution using Intel® hardware and software tools. The app will detect people in a designated area, providing the number of people in the frame, average duration of people in frame, and total count. This project is a part of Intel Edge AI for IOT Developers Nanodegree program by udacity. 4 | 5 | Youtube Link- https://youtu.be/E9hvkBaylk0 6 | 7 | ![screenshot_1](https://user-images.githubusercontent.com/34116562/80679006-ab9dc000-8ad9-11ea-9756-1fdb898276b2.png) 8 | 9 | 10 | ### How it works? 11 | 12 | The counter will use the Inference Engine included in the Intel® Distribution of OpenVINO™ Toolkit. The model used should be able to identify people in a video frame. The application should count the number of people in the current frame, the duration that a person is in the frame (time elapsed between entering and exiting a frame) and the total count of people. It then sends the data to a local web server using the Paho MQTT Python package. 13 | 14 | 15 | ![architectural diagram](./images/arch_diagram.png) 16 | 17 | 18 | ## Requirements 19 | 20 | #### Hardware 21 | 22 | * 6th to 10th generation Intel® Core™ processor with Iris® Pro graphics or Intel® HD Graphics. 23 | * OR use of Intel® Neural Compute Stick 2 (NCS2) 24 | 25 | #### Software 26 | 27 | * Intel® Distribution of OpenVINO™ toolkit 2019 R3 release 28 | * Node v6.17.1 29 | * Npm v3.10.10 30 | * CMake 31 | * MQTT Mosca server 32 | * Python 3.5 or 3.6 33 | 34 | 35 | ### Explaining Model Selection & Custom Layers 36 | 37 | TensorFlow Object Detection Model Zoo (https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md) contains many pre-trained models on the coco dataset. Ssd_inception_v2_coco and faster_rcnn_inception_v2_coco performed good as compared to rest of the models, but, in this project, faster_rcnn_inception_v2_coco is used which is fast in detecting people with less errors. Intel openVINO already contains extensions for custom layers used in TensorFlow Object Detection Model Zoo. 38 | 39 | Downloading the model from the GitHub repository of Tensorflow Object Detection Model Zoo by the following command: 40 | 41 | ``` 42 | wget http://download.tensorflow.org/models/object_detection/faster_rcnn_inception_v2_coco_2018_01_28.tar.gz 43 | ``` 44 | Extracting the tar.gz file by the following command: 45 | 46 | ``` 47 | tar -xvf faster_rcnn_inception_v2_coco_2018_01_28.tar.gz 48 | ``` 49 | Changing the directory to the extracted folder of the downloaded model: 50 | 51 | ``` 52 | cd faster_rcnn_inception_v2_coco_2018_01_28 53 | ``` 54 | The model can't be the existing models provided by Intel. So, converting the TensorFlow model to Intermediate Representation (IR) or OpenVINO IR format. The command used is given below: 55 | 56 | ``` 57 | python /opt/intel/openvino/deployment_tools/model_optimizer/mo.py --input_model faster_rcnn_inception_v2_coco_2018_01_28/frozen_inference_graph.pb --tensorflow_object_detection_api_pipeline_config pipeline.config --reverse_input_channels --tensorflow_use_custom_operations_config /opt/intel/openvino/deployment_tools/model_optimizer/extensions/front/tf/faster_rcnn_support.json 58 | ``` 59 | 60 | ### Comparing Model Performance 61 | 62 | Model-1: Ssd_inception_v2_coco_2018_01_28 63 | 64 | Converted the model to intermediate representation using the following command. Further, this model lacked accuracy as it didn't detect people correctly in the video. Made some alterations to the threshold for increasing its accuracy but the results were not fruitful. 65 | ``` 66 | python /opt/intel/openvino/deployment_tools/model_optimizer/mo.py --input_model ssd_inception_v2_coco_2018_01_28/frozen_inference_graph.pb --tensorflow_object_detection_api_pipeline_config pipeline.config --reverse_input_channels --tensorflow_use_custom_operations_config /opt/intel/openvino/deployment_tools/model_optimizer/extensions/front/tf/ssd_v2_support.json 67 | ``` 68 | 69 | Model-2: Faster_rcnn_inception_v2_coco_2018_01_28 70 | 71 | Converted the model to intermediate representation using the following command. Model -2 i.e. Faster_rcnn_inception_v2_coco, performed really well in the output video. After using a threshold of 0.4, the model works better than all the previous approaches. 72 | ``` 73 | python /opt/intel/openvino/deployment_tools/model_optimizer/mo.py --input_model faster_rcnn_inception_v2_coco_2018_01_28/frozen_inference_graph.pb --tensorflow_object_detection_api_pipeline_config pipeline.config --reverse_input_channels --tensorflow_use_custom_operations_config /opt/intel/openvino/deployment_tools/model_optimizer/extensions/front/tf/faster_rcnn_support.json 74 | ``` 75 | ##### Comparison 76 | 77 | Comparing the two models i.e. ssd_inception_v2_coco and faster_rcnn_inception_v2_coco in terms of latency and memory, several insights were drawn. It could be clearly seen that the Latency (microseconds) and Memory (Mb) decreases in case of OpenVINO as compared to plain Tensorflow model which is very useful in case of OpenVINO applications. 78 | 79 | | Model/Framework | Latency (microseconds) | Memory (Mb) | 80 | | ----------------------------------- |:---------------------------------:| -------:| 81 | | ssd_inception_v2_coco (plain TF) | 222 | 538 | 82 | | ssd_inception_v2_coco (OpenVINO) | 155 | 329 | 83 | | faster_rcnn_inception_v2_coco (plain TF) | 1281 | 562 | 84 | | faster_rcnn_inception_v2_coco (OpenVINO) | 889 | 281 | 85 | 86 | ##### Differences in Edge and Cloud computing 87 | 88 | Edge Computing is regarded as ideal for operations with extreme latency concerns. Thus, medium scale companies that have budget limitations can use edge computing to save financial resources. Cloud Computing is more suitable for projects and organizations which deal with massive data storage. 89 | 90 | ### Model Use Cases 91 | 92 | This application could keep a check on the number of people in a particular area and could be helpful where there is restriction on the number of people present in a particular area. Further, with some updations, this could also prove helpful in the current COVID-19 scenario i.e. to keep a check on the number of people in the frame. 93 | 94 | ### Effects on End user needs 95 | 96 | Various insights could be drawn on the model by testing it with different videos and analyzing the model performance on low light input videos. This would be an important factor in determining the best model for the given scenario. 97 | 98 | ### Running the Main Application 99 | 100 | After converting the downloaded model to the OpenVINO IR, all the three servers can be started on separate terminals i.e. 101 | 102 | - MQTT Mosca server 103 | - Node.js* Web server 104 | - FFmpeg server 105 | 106 | 107 | #### Setting up the environment 108 | 109 | Configuring the environment to use the Intel® Distribution of OpenVINO™ toolkit one time per session by running the following command: 110 | ``` 111 | source /opt/intel/openvino/bin/setupvars.sh -pyver 3.5 112 | ``` 113 | 114 | Further, from the main directory: 115 | 116 | #### Step 1 - Start the Mosca server 117 | 118 | ``` 119 | cd webservice/server/node-server 120 | node ./server.js 121 | ``` 122 | 123 | The following message is displayed, if successful: 124 | ``` 125 | Mosca server started. 126 | ``` 127 | 128 | #### Step 2 - Start the GUI 129 | 130 | Opening new terminal and executing below commands: 131 | ``` 132 | cd webservice/ui 133 | npm run dev 134 | ``` 135 | 136 | The following message is displayed, if successful: 137 | ``` 138 | webpack: Compiled successfully 139 | ``` 140 | 141 | #### Step 3 - FFmpeg Server 142 | 143 | Opening new terminal and executing below command: 144 | ``` 145 | sudo ffserver -f ./ffmpeg/server.conf 146 | ``` 147 | 148 | #### Step 4 - Run the code 149 | 150 | Opening new terminal and executing below command: 151 | ``` 152 | python main.py -i resources/Pedestrian_Detect_2_1_1.mp4 -m faster_rcnn_inception_v2_coco_2018_01_28/frozen_inference_graph.xml -l /opt/intel/openvino/deployment_tools/inference_engine/lib/intel64/libcpu_extension_sse4.so -d CPU -pt 0.4 | ffmpeg -v warning -f rawvideo -pixel_format bgr24 -video_size 768x432 -framerate 24 -i - http://0.0.0.0:3004/fac.ffm 153 | ``` 154 | 155 | ### Screenshots: 156 | 157 | ![Screenshot from 2020-04-30 11-50-23](https://user-images.githubusercontent.com/34116562/80721855-5a62f000-8b1c-11ea-99d6-e702766778a1.png) 158 | ![Screenshot from 2020-04-30 11-50-43](https://user-images.githubusercontent.com/34116562/80721862-5c2cb380-8b1c-11ea-9a54-9f0ad004a61b.png) 159 | ![Screenshot from 2020-04-30 11-51-26](https://user-images.githubusercontent.com/34116562/80721867-5d5de080-8b1c-11ea-9a4e-ab1a2e79af88.png) 160 | ![Screenshot from 2020-04-30 11-50-30](https://user-images.githubusercontent.com/34116562/80721874-5fc03a80-8b1c-11ea-94e5-884873a9719f.png) 161 | ![Screenshot from 2020-04-30 11-50-58](https://user-images.githubusercontent.com/34116562/80721880-62229480-8b1c-11ea-8fc0-46d5be652465.png) 162 | ![Screenshot from 2020-04-30 11-51-35](https://user-images.githubusercontent.com/34116562/80721882-6353c180-8b1c-11ea-84df-ce3a2a940b9f.png) 163 | -------------------------------------------------------------------------------- /WRITEUP.md: -------------------------------------------------------------------------------- 1 | # Project Write-Up 2 | 3 | You can use this document as a template for providing your project write-up. However, if you 4 | have a different format you prefer, feel free to use it as long as you answer all required 5 | questions. 6 | 7 | ## Explaining Custom Layers 8 | 9 | The process behind converting custom layers involves... 10 | 11 | Some of the potential reasons for handling custom layers are... 12 | 13 | ## Comparing Model Performance 14 | 15 | My method(s) to compare models before and after conversion to Intermediate Representations 16 | were... 17 | 18 | The difference between model accuracy pre- and post-conversion was... 19 | 20 | The size of the model pre- and post-conversion was... 21 | 22 | The inference time of the model pre- and post-conversion was... 23 | 24 | ## Assess Model Use Cases 25 | 26 | Some of the potential use cases of the people counter app are... 27 | 28 | Each of these use cases would be useful because... 29 | 30 | ## Assess Effects on End User Needs 31 | 32 | Lighting, model accuracy, and camera focal length/image size have different effects on a 33 | deployed edge model. The potential effects of each of these are as follows... 34 | 35 | ## Model Research 36 | 37 | [This heading is only required if a suitable model was not found after trying out at least three 38 | different models. However, you may also use this heading to detail how you converted 39 | a successful model.] 40 | 41 | In investigating potential people counter models, I tried each of the following three models: 42 | 43 | - Model 1: [Name] 44 | - [Model Source] 45 | - I converted the model to an Intermediate Representation with the following arguments... 46 | - The model was insufficient for the app because... 47 | - I tried to improve the model for the app by... 48 | 49 | - Model 2: [Name] 50 | - [Model Source] 51 | - I converted the model to an Intermediate Representation with the following arguments... 52 | - The model was insufficient for the app because... 53 | - I tried to improve the model for the app by... 54 | 55 | - Model 3: [Name] 56 | - [Model Source] 57 | - I converted the model to an Intermediate Representation with the following arguments... 58 | - The model was insufficient for the app because... 59 | - I tried to improve the model for the app by... 60 | -------------------------------------------------------------------------------- /fac.ffm: -------------------------------------------------------------------------------- 1 | FFM2MAIN}COMM}S2VIab=8192000,bufsize=67108864,time_base=1/25,video_size=hd480,strict=-1,bt=2048000,maxrate=16384000CPRVrc_eq=tex^qComp -------------------------------------------------------------------------------- /ffmpeg/server.conf: -------------------------------------------------------------------------------- 1 | # Server 2 | HTTPPort 3004 3 | HTTPBindAddress 0.0.0.0 4 | MaxHTTPConnections 200 5 | MaxClients 100 6 | MaxBandwidth 54000 7 | CustomLog - 8 | 9 | # Feed/Raw video 10 | 11 | File fac.ffm 12 | FileMaxSize 16M 13 | ACL allow 127.0.0.1 14 | 15 | 16 | # Stream 17 | 18 | Feed fac.ffm 19 | Format mpjpeg 20 | VideoBitRate 8192 21 | VideoBufferSize 8192 22 | VideoFrameRate 25 23 | VideoSize hd480 24 | #VideoQMin 2 25 | #VideoQMax 8 26 | NoAudio 27 | Strict -1 28 | 29 | ACL allow 192.168.0.0 192.168.255.255 30 | ACL allow localhost 31 | ACL allow 127.0.0.1 32 | 33 | 34 | # Special streams 35 | # Server status 36 | 37 | Format status 38 | ACL allow localhost 39 | ACL allow 127.0.0.1 40 | ACL allow 192.168.0.0 192.168.255.255 41 | 42 | 43 | # Redirect index.html to the appropriate site 44 | 45 | URL http://www.github.com/intel-iot-devkit 46 | 47 | -------------------------------------------------------------------------------- /images/arch_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/images/arch_diagram.png -------------------------------------------------------------------------------- /images/jupy1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/images/jupy1.png -------------------------------------------------------------------------------- /images/jupy2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/images/jupy2.png -------------------------------------------------------------------------------- /images/people-counter-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/images/people-counter-image.png -------------------------------------------------------------------------------- /inference.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Copyright (c) 2018 Intel Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | """ 24 | 25 | import os 26 | import sys 27 | import logging as log 28 | from openvino.inference_engine import IENetwork, IECore 29 | 30 | 31 | 32 | class Network: 33 | """ 34 | Load and configure inference plugins for the specified target devices 35 | and performs synchronous and asynchronous modes for the specified infer requests. 36 | """ 37 | 38 | def __init__(self): 39 | ### TODO: Initialize any class variables desired ### 40 | self.plugin = None 41 | self.network = None 42 | self.input_blob = None 43 | self.output_blob = None 44 | self.exec_network = None 45 | self.infer_request = None 46 | 47 | 48 | def load_model(self, model, CPU_EXTENSION, DEVICE, console_output= False): 49 | ### TODO: Load the model ### 50 | model_xml = model 51 | model_bin = os.path.splitext(model_xml)[0] + ".bin" 52 | 53 | self.plugin = IECore() 54 | self.network = IENetwork(model=model_xml, weights=model_bin) 55 | ### TODO: Check for supported layers ### 56 | if not all_layers_supported(self.plugin, self.network, console_output=console_output): 57 | self.plugin.add_extension(CPU_EXTENSION, DEVICE) 58 | 59 | self.exec_network = self.plugin.load_network(self.network, DEVICE) 60 | # Get the input layer 61 | self.input_blob = next(iter(self.network.inputs)) 62 | self.output_blob = next(iter(self.network.outputs)) 63 | 64 | ### TODO: Return the loaded inference plugin ### 65 | ### Note: You may need to update the function parameters. ### 66 | return 67 | 68 | def get_input_shape(self): 69 | ### TODO: Return the shape of the input layer ### 70 | input_shapes = {} 71 | for inp in self.network.inputs: 72 | input_shapes[inp] = (self.network.inputs[inp].shape) 73 | return input_shapes 74 | 75 | 76 | def exec_net(self, net_input, request_id): 77 | ### TODO: Start an asynchronous request ### 78 | ### TODO: Return any necessary information ### 79 | self.infer_request_handle = self.exec_network.start_async( 80 | request_id, 81 | inputs=net_input) 82 | 83 | return 84 | 85 | 86 | def wait(self): 87 | ### TODO: Wait for the request to be complete. ### 88 | ### TODO: Return any necessary information ### 89 | ### Note: You may need to update the function parameters. ### 90 | status = self.infer_request_handle.wait() 91 | return status 92 | 93 | 94 | def get_output(self): 95 | ### TODO: Extract and return the output results 96 | ### Note: You may need to update the function parameters. ### 97 | out = self.infer_request_handle.outputs[self.output_blob] 98 | return out 99 | 100 | def all_layers_supported(engine, network, console_output=False): 101 | ### TODO check if all layers are supported 102 | ### return True if all supported, False otherwise 103 | layers_supported = engine.query_network(network, device_name='CPU') 104 | layers = network.layers.keys() 105 | 106 | all_supported = True 107 | for l in layers: 108 | if l not in layers_supported: 109 | all_supported = False 110 | 111 | return all_supported 112 | -------------------------------------------------------------------------------- /linux-setup.md: -------------------------------------------------------------------------------- 1 | # Linux - Initial Setup 2 | 3 | ### Install Intel® Distribution of OpenVINO™ Toolkit 4 | 5 | Refer to [this page](https://software.intel.com/en-us/articles/OpenVINO-Install-Linux) for more information about how to install and setup the Intel® Distribution of OpenVINO™ Toolkit. 6 | 7 | You will need the OpenCL™ Runtime Package if you plan to run inference on the GPU. It is not mandatory for CPU inference. 8 | 9 | ### Install Nodejs and its dependencies 10 | 11 | - This step is only required if the user previously used Chris Lea's Node.js PPA. 12 | 13 | ``` 14 | sudo add-apt-repository -y -r ppa:chris-lea/node.js 15 | sudo rm -f /etc/apt/sources.list.d/chris-lea-node_js-*.list 16 | sudo rm -f /etc/apt/sources.list.d/chris-lea-node_js-*.list.save 17 | ``` 18 | - To install Nodejs and Npm, run the below commands: 19 | ``` 20 | curl -sSL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | sudo apt-key add - 21 | VERSION=node_6.x 22 | DISTRO="$(lsb_release -s -c)" 23 | echo "deb https://deb.nodesource.com/$VERSION $DISTRO main" | sudo tee /etc/apt/sources.list.d/nodesource.list 24 | echo "deb-src https://deb.nodesource.com/$VERSION $DISTRO main" | sudo tee -a /etc/apt/sources.list.d/nodesource.list 25 | sudo apt-get update 26 | sudo apt-get install nodejs 27 | ``` 28 | 29 | ### Install the following dependencies 30 | 31 | ``` 32 | sudo apt update 33 | sudo apt-get install python3-pip 34 | pip3 install numpy 35 | pip3 install paho-mqtt 36 | sudo apt install libzmq3-dev libkrb5-dev 37 | sudo apt install ffmpeg 38 | sudo apt-get install cmake 39 | ``` 40 | 41 | If you’re prompted to upgrade pip, do not update. 42 | 43 | ### Install NPM 44 | 45 | Follow the instructions in the main README file under “Install npm” to make sure the relevant NPM libraries are installed for the included Node servers. 46 | -------------------------------------------------------------------------------- /mac-setup.md: -------------------------------------------------------------------------------- 1 | # Mac - Initial Setup 2 | 3 | ### Install Intel® Distribution of OpenVINO™ Toolkit 4 | 5 | Refer to [this page](https://docs.openvinotoolkit.org/latest/_docs_install_guides_installing_openvino_macos.html) for more information about how to install and setup the Intel® Distribution of OpenVINO™ Toolkit for MacOS. 6 | 7 | ### Install Nodejs and its dependencies 8 | 9 | - Navigate to the [Node.js download page](https://nodejs.org/en/download/) and install the MacOS version. This should also install `npm` along with `node`. 10 | - Verify installation in a terminal with `node -v` and `npm -v` (it should show the installed version). 11 | 12 | ### Install the following dependencies 13 | 14 | First, make sure you have [Homebrew](https://brew.sh/) installed on your machine. 15 | 16 | Then, download a version of Python 3.5 from [here](https://www.python.org/downloads/). The project code is pre-compiled with the OpenVINO™ Toolkit to best work with Python 3.5. You can then follow the additional steps [here](https://evansdianga.com/install-pip-osx/) to add `pip3` on your Mac (making sure you stick with Python 3.5 - you do not need to install this again). 17 | 18 | Next, run the following from the terminal: 19 | 20 | ``` 21 | pip3 install numpy 22 | pip3 install paho-mqtt 23 | brew install cmake 24 | brew install zeromq 25 | ``` 26 | 27 | #### FFmpeg 28 | 29 | This project makes use of FFmpeg’s `ffserver` functionality, which was deprecated in an older version. As such, a new install of ffmpeg will not include it if you do it directly from `brew. You can use the below to install the older version containing `ffserver`, as detailed in [this post](https://superuser.com/questions/1296377/why-am-i-getting-an-unable-to-find-a-suitable-output-format-for-http-localho/1297419#1297419). 30 | 31 | ``` 32 | git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg 33 | cd ffmpeg 34 | git checkout 2ca65fc7b74444edd51d5803a2c1e05a801a6023 35 | ./configure 36 | make -j4 37 | ``` 38 | 39 | ### Install NPM 40 | 41 | Follow the instructions in the main README file under “Install npm” to make sure the relevant NPM libraries are installed for the included Node servers. 42 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | """People Counter.""" 2 | """ 3 | Copyright (c) 2018 Intel Corporation. 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit person to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 16 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 17 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 18 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 19 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | """ 21 | import time 22 | import socket 23 | import json 24 | import cv2 25 | import os 26 | import sys 27 | import numpy as np 28 | import logging as log 29 | import paho.mqtt.client as mqtt 30 | 31 | from argparse import ArgumentParser 32 | from inference import Network 33 | 34 | # MQTT server environment variables 35 | HOSTNAME = socket.gethostname() 36 | IPADDRESS = socket.gethostbyname(HOSTNAME) 37 | MQTT_HOST = IPADDRESS 38 | MQTT_PORT = 3001 39 | MQTT_KEEPALIVE_INTERVAL = 60 40 | 41 | 42 | 43 | def build_argparser(): 44 | """ 45 | Parse command line arguments. 46 | 47 | :return: command line arguments 48 | """ 49 | parser = ArgumentParser() 50 | parser.add_argument("-m", "--model", required=True, type=str, 51 | help="Path to an xml file with a trained model.") 52 | parser.add_argument("-i", "--input", required=True, type=str, 53 | help="Path to image or video file") 54 | parser.add_argument("-l", "--cpu_extension", required=False, type=str, 55 | default=None, 56 | help="MKLDNN (CPU)-targeted custom layers." 57 | "Absolute path to a shared library with the" 58 | "kernels impl.") 59 | parser.add_argument("-d", "--device", type=str, default="CPU", 60 | help="Specify the target device to infer on: " 61 | "CPU, GPU, FPGA or MYRIAD is acceptable. Sample " 62 | "will look for a suitable plugin for device " 63 | "specified (CPU by default)") 64 | parser.add_argument("-pt", "--prob_threshold", type=float, default=0.5, 65 | help="Probability threshold for detections filtering" 66 | "(0.5 by default)") 67 | return parser 68 | 69 | 70 | 71 | def connect_mqtt(): 72 | ### TODO: Connect to the MQTT client ### 73 | client = mqtt.Client() 74 | client.connect(MQTT_HOST, MQTT_PORT, MQTT_KEEPALIVE_INTERVAL) 75 | return client 76 | 77 | 78 | 79 | def infer_on_stream(args, client): 80 | """ 81 | Initialize the inference network, stream video to network, 82 | and output stats and video. 83 | :param args: Command line arguments parsed by `build_argparser()` 84 | :param client: MQTT client 85 | :return: None 86 | """ 87 | # Initialise the class 88 | infer_network = Network() 89 | # Set Probability threshold for detections 90 | prob_threshold = args.prob_threshold 91 | model = args.model 92 | 93 | DEVICE = args.device 94 | CPU_EXTENSION = args.cpu_extension 95 | 96 | ### TODO: Load the model through `infer_network` ### 97 | infer_network.load_model(model, CPU_EXTENSION, DEVICE) 98 | network_shape = infer_network.get_input_shape() 99 | 100 | ### TODO: Handle the input stream ### 101 | # Checks for live feed 102 | if args.input == 'CAM': 103 | input_validated = 0 104 | 105 | # Checks for input image 106 | elif args.input.endswith('.jpg') or args.input.endswith('.bmp') : 107 | single_image_mode = True 108 | input_validated = args.input 109 | 110 | # Checks for video file 111 | else: 112 | input_validated = args.input 113 | assert os.path.isfile(args.input), "file doesn't exist" 114 | 115 | ### TODO: Handle the input stream ### 116 | cap = cv2.VideoCapture(input_validated) 117 | cap.open(input_validated) 118 | 119 | w = int(cap.get(3)) 120 | h = int(cap.get(4)) 121 | 122 | in_shape = network_shape['image_tensor'] 123 | 124 | #iniatilize variables 125 | 126 | duration_prev = 0 127 | counter_total = 0 128 | dur = 0 129 | request_id=0 130 | 131 | report = 0 132 | counter = 0 133 | counter_prev = 0 134 | 135 | 136 | ### TODO: Loop until stream is over ### 137 | while cap.isOpened(): 138 | ### TODO: Read from the video capture ### 139 | flag, frame = cap.read() 140 | if not flag: 141 | break 142 | 143 | ### TODO: Pre-process the image as needed ### 144 | image = cv2.resize(frame, (in_shape[3], in_shape[2])) 145 | image_p = image.transpose((2, 0, 1)) 146 | image_p = image_p.reshape(1, *image_p.shape) 147 | 148 | 149 | ### TODO: Start asynchronous inference for specified request ### 150 | net_input = {'image_tensor': image_p,'image_info': image_p.shape[1:]} 151 | duration_report = None 152 | infer_network.exec_net(net_input, request_id) 153 | 154 | ### TODO: Wait for the result ### 155 | if infer_network.wait() == 0: 156 | 157 | ### TODO: Get the results of the inference request ### 158 | net_output = infer_network.get_output() 159 | 160 | ### TODO: Extract any desired stats from the results ### 161 | 162 | pointer = 0 163 | probs = net_output[0, 0, :, 2] 164 | for i, p in enumerate(probs): 165 | if p > prob_threshold: 166 | pointer += 1 167 | box = net_output[0, 0, i, 3:] 168 | p1 = (int(box[0] * w), int(box[1] * h)) 169 | p2 = (int(box[2] * w), int(box[3] * h)) 170 | frame = cv2.rectangle(frame, p1, p2, (0, 255, 0), 3) 171 | 172 | if pointer != counter: 173 | counter_prev = counter 174 | counter = pointer 175 | if dur >= 3: 176 | duration_prev = dur 177 | dur = 0 178 | else: 179 | dur = duration_prev + dur 180 | duration_prev = 0 # unknown, not needed in this case 181 | else: 182 | dur += 1 183 | if dur >= 3: 184 | report = counter 185 | if dur == 3 and counter > counter_prev: 186 | counter_total += counter - counter_prev 187 | elif dur == 3 and counter < counter_prev: 188 | duration_report = int((duration_prev / 10.0) * 1000) 189 | 190 | ### TODO: Calculate and send relevant information on ### 191 | ### current_count, total_count and duration to the MQTT server ### 192 | ### Topic "person": keys of "count" and "total" ### 193 | ### Topic "person/duration": key of "duration" ### 194 | client.publish('person', 195 | payload=json.dumps({ 196 | 'count': report, 'total': counter_total}), 197 | qos=0, retain=False) 198 | if duration_report is not None: 199 | client.publish('person/duration', 200 | payload=json.dumps({'duration': duration_report}), 201 | qos=0, retain=False) 202 | 203 | 204 | ### TODO: Send the frame to the FFMPEG server ### 205 | # Resize the frame 206 | frame = cv2.resize(frame, (768, 432)) 207 | sys.stdout.buffer.write(frame) 208 | sys.stdout.flush() 209 | 210 | cap.release() 211 | cv2.destroyAllWindows() 212 | 213 | 214 | 215 | def main(): 216 | """ 217 | Load the network and parse the output. 218 | 219 | :return: None 220 | """ 221 | # Grab command line args 222 | args = build_argparser().parse_args() 223 | # Connect to the MQTT server 224 | client = connect_mqtt() 225 | # Perform inference on the input stream 226 | infer_on_stream(args, client) 227 | 228 | 229 | if __name__ == '__main__': 230 | main() 231 | -------------------------------------------------------------------------------- /resources/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/resources/.DS_Store -------------------------------------------------------------------------------- /resources/Pedestrian_Detect_2_1_1.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/resources/Pedestrian_Detect_2_1_1.mp4 -------------------------------------------------------------------------------- /webservice/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/.DS_Store -------------------------------------------------------------------------------- /webservice/server/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/server/.DS_Store -------------------------------------------------------------------------------- /webservice/server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /webservice/server/README.md: -------------------------------------------------------------------------------- 1 | # intel-facial-recognition-server 2 | 3 | ## Launching the server 4 | Tested on Node versions 4.2.6 and 8.5. 5 | 6 | Get node.js and NPM up and running then... 7 | 8 | Install the Node.js dependencies (uuid4 mosca): 9 | ~~~~ 10 | npm install 11 | ~~~~ 12 | 13 | Start the server: 14 | ~~~~ 15 | cd node-server 16 | node ./server.js 17 | ~~~~ 18 | 19 | ## API Documentation 20 | [https://scenarios.stoplight.io/amplified-by-design/specs/facial-recognition] 21 | 22 | ## MQTT 23 | Mosca MQTT broker is automatically started on port 3001. This can be changed in the configuration file. 24 | For testing or debugging the MQTT broker, if necessary, you can use: 25 | * [mosquitto-clients](https://mosquitto.org/download/) 26 | * [mqtt-spy](http://kamilfb.github.io/mqtt-spy/) 27 | 28 | -------------------------------------------------------------------------------- /webservice/server/node-server/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/server/node-server/.DS_Store -------------------------------------------------------------------------------- /webservice/server/node-server/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mqtt: { 3 | port: 3001, 4 | host: 'localhost', 5 | http: {port: 3002, bundle: true, static: './'} 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /webservice/server/node-server/mqtt.js: -------------------------------------------------------------------------------- 1 | var config; 2 | var server; 3 | const mosca = require('mosca'); 4 | 5 | module.exports = { 6 | configure: function (c) { 7 | config = c; 8 | }, 9 | 10 | start: function () { 11 | server = new mosca.Server({ 12 | port: config.mqtt.port, 13 | http: config.mqtt.http, 14 | }); 15 | 16 | server.on('ready', setup); 17 | server.on('clientConnected', connected); 18 | server.on('clientDisconnected', disconnected); 19 | server.on('published', published); 20 | server.on('subscribed', subscribed); 21 | server.on('unsubscribed', unsubscribed); 22 | }, 23 | 24 | publish: function (topic, message) { 25 | var payload = { 26 | topic: topic, 27 | payload: message, 28 | qos: 0, 29 | retain: false 30 | }; 31 | 32 | server.publish(payload, function () { 33 | console.log('Published callback complete.'); 34 | }); 35 | } 36 | }; 37 | 38 | function setup() { 39 | console.log('Mosca server started.'); 40 | } 41 | 42 | function connected(client) { 43 | console.log(`Client ${client.id} connected`); 44 | } 45 | 46 | function subscribed(topic, client) { 47 | console.log(`Client ${client.id} subscribed to ${topic}.`); 48 | } 49 | 50 | function unsubscribed(topic, client) { 51 | console.log(`Client ${client.id} unsubscribed from ${topic}.`); 52 | } 53 | 54 | function disconnected(client) { 55 | console.log(`Client ${client.id}`); 56 | } 57 | 58 | function published(packet, client) { 59 | console.log(`Published to ${packet.topic} <- ${packet.payload}`); 60 | } 61 | -------------------------------------------------------------------------------- /webservice/server/node-server/server.js: -------------------------------------------------------------------------------- 1 | const config = require('./config'); 2 | 3 | /* Start the MQTT server */ 4 | mqtt = require('./mqtt'); 5 | mqtt.configure(config); 6 | mqtt.start(); -------------------------------------------------------------------------------- /webservice/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "intel-people-counter-server", 3 | "version": "1.0.0", 4 | "description": "Node server to support api calls from the people counter UI.", 5 | "main": "./server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/udacity" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "homepage": "https://www.udacity.com", 16 | "dependencies": { 17 | "mosca": "^2.5.2", 18 | "uuid4": "^1.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /webservice/ui/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "react", 4 | "es2015", 5 | "stage-1" 6 | ] 7 | } -------------------------------------------------------------------------------- /webservice/ui/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "node": true, 6 | "mocha": true 7 | }, 8 | "extends": "airbnb", 9 | "parser": "babel-eslint", 10 | "settings": { 11 | "import/resolver": { 12 | "webpack": { 13 | "config": "webpack.config.js" 14 | } 15 | } 16 | }, 17 | "rules": { 18 | // indentation 19 | "indent": [ 2 ], 20 | 21 | // spacing 22 | "space-in-parens": [ 2, "always" ], 23 | "template-curly-spacing": [ 2, "always" ], 24 | "array-bracket-spacing": [ 2, "always" ], 25 | "object-curly-spacing": [ 2, "always" ], 26 | "computed-property-spacing": [ 2, "always" ], 27 | "no-multiple-empty-lines": [ 2, { "max": 1, "maxEOF": 0, "maxBOF": 0 } ], 28 | 29 | // strings 30 | "quotes": [ 2, "double", "avoid-escape" ], 31 | 32 | // code arrangement matter 33 | "no-use-before-define": [ 2, { "functions": false } ], 34 | 35 | // make it meaningful 36 | "prefer-const": 1, 37 | 38 | // keep it simple 39 | "complexity": [ 1, 5 ], 40 | 41 | // react 42 | "react/prefer-es6-class": 0, 43 | "react/jsx-filename-extension": 0, 44 | "react/jsx-curly-spacing": [ 2, "always" ], 45 | "react/jsx-indent": [ 2 ] 46 | } 47 | } -------------------------------------------------------------------------------- /webservice/ui/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | .DS_Store -------------------------------------------------------------------------------- /webservice/ui/README.md: -------------------------------------------------------------------------------- 1 | # Intel People Counter 2 | Based on the Amplified starter kit, which uses... 3 | 4 | - Webpack 2 5 | - React 6 | - Redux 7 | - Redux Thunk 8 | - React Redux Router 9 | - CSS Next 10 | - Jest + Enzyme 11 | - AirBnB ESLint 12 | 13 | 14 | ### Requirements: 15 | - Node 7.7.4+ 16 | - Npm 4.1.2+ 17 | 18 | 19 | ### Setup 20 | Update to latest packages: 21 | ``` 22 | npm update --save --dev 23 | ``` 24 | 25 | Install dependencies: 26 | ``` 27 | npm install 28 | ``` 29 | 30 | 31 | ### Development 32 | Start a local development server at http://localhost:3000/, build project, and watch files. 33 | ``` 34 | npm run dev 35 | ``` 36 | > You can access the dev build from any device on the network through your network IP address. If you don't know your IP, use this tool to find it https://www.npmjs.com/package/dev-ip 37 | 38 | 39 | ### Folder Structure 40 | __assets__ 41 | > Contains all static assets. Create folders for fonts, images, and anything else. 42 | 43 | __components__ 44 | > Small and highly reusable interface elements, these should rarely if ever be connected to redux. 45 | 46 | __constants__ 47 | > Application constants for both js and css. 48 | 49 | __dux__ 50 | > Short for redux in ducks style, there is generally one dux file per feature. Each dux file contains actions, action creators, and a reducer. 51 | 52 | __features__ 53 | > Larger and more complex interface elements which generally do not get re-used. These will often be connected to redux via a Connected wrapper file. 54 | 55 | __pages__ 56 | > Entire application views which are rendered by the router. 57 | 58 | 59 | ### Styles 60 | This repo does not include any CSS libraries. 61 | For a total CSS reset use https://www.npmjs.com/package/reset-css 62 | ``` 63 | npm install --save reset-css 64 | ``` 65 | 66 | Otherwise install normalize https://www.npmjs.com/package/normalize.css/ 67 | ``` 68 | npm install --save normalize.css 69 | ``` 70 | -------------------------------------------------------------------------------- /webservice/ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "intel-people-counter-app", 3 | "version": "0.1.0", 4 | "description": "Intel People Counter App project", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "cross-env NODE_ENV=development webpack-dev-server --history-api-fallback --watch --hot --inline", 8 | "dist": "cross-env NODE_ENV=production webpack -p", 9 | "test": "jest" 10 | }, 11 | "author": "amplified", 12 | "license": "ISC", 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/intel-iot-devkit/people-counter-python.git" 16 | }, 17 | "devDependencies": { 18 | "babel-core": "^6.25.0", 19 | "babel-eslint": "^7.2.3", 20 | "babel-jest": "^20.0.3", 21 | "babel-loader": "^6.4.1", 22 | "babel-preset-env": "^1.6.0", 23 | "babel-preset-es2015": "^6.24.0", 24 | "babel-preset-react": "^6.23.0", 25 | "babel-preset-stage-1": "^6.22.0", 26 | "clean-webpack-plugin": "^0.1.16", 27 | "copy-webpack-plugin": "^4.0.1", 28 | "cross-env": "^3.2.4", 29 | "css-loader": "^0.27.3", 30 | "enzyme": "^2.9.1", 31 | "eslint": "^3.19.0", 32 | "eslint-config-airbnb": "^15.1.0", 33 | "eslint-import-resolver-webpack": "^0.8.3", 34 | "eslint-plugin-import": "^2.7.0", 35 | "eslint-plugin-jsx-a11y": "^5.1.1", 36 | "eslint-plugin-react": "^7.1.0", 37 | "extract-text-webpack-plugin": "^2.1.2", 38 | "file-loader": "^0.10.1", 39 | "html-loader": "^0.4.5", 40 | "html-webpack-plugin": "^2.29.0", 41 | "identity-obj-proxy": "^3.0.0", 42 | "jest": "^20.0.4", 43 | "postcss": "^5.2.16", 44 | "postcss-cssnext": "^2.10.0", 45 | "postcss-import": "^9.1.0", 46 | "postcss-loader": "^1.3.3", 47 | "react-test-renderer": "^15.6.1", 48 | "redux-devtools-extension": "^2.13.2", 49 | "style-loader": "^0.16.0", 50 | "url-loader": "^0.5.9", 51 | "webpack": "^2.7.0", 52 | "webpack-dev-server": "^3.1.11", 53 | "webpack-merge": "^4.1.0" 54 | }, 55 | "dependencies": { 56 | "chart.js": "^2.7.0", 57 | "classnames": "^2.2.5", 58 | "moment": "^2.18.1", 59 | "mqtt": "^2.13.0", 60 | "postcss-mixins": "^6.1.0", 61 | "prop-types": "^15.5.10", 62 | "react": "^15.6.1", 63 | "react-chartjs-2": "^2.6.2", 64 | "react-dom": "^15.6.1", 65 | "react-fontawesome": "^1.6.1", 66 | "react-redux": "^5.0.6", 67 | "react-router": "^4.2.0", 68 | "react-router-dom": "^4.2.2", 69 | "react-router-redux": "^5.0.0-alpha.6", 70 | "redux": "^3.7.2", 71 | "redux-thunk": "^2.2.0" 72 | }, 73 | "jest": { 74 | "moduleNameMapper": { 75 | "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", 76 | "\\.(css|less|scss)$": "identity-obj-proxy" 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /webservice/ui/src/MqttClient.js: -------------------------------------------------------------------------------- 1 | import mqtt from "mqtt"; 2 | import { MQTT } from "./constants/constants"; 3 | 4 | class MqttClient { 5 | constructor() { 6 | 7 | // connect to mqtt server 8 | this.client = mqtt.connect( MQTT.MQTT_SERVER ); 9 | 10 | // save subscribes here with topics as keys and handlers as values 11 | this.handlers = []; 12 | 13 | // listen for successful connection 14 | this.client.on( "connect", () => { 15 | // subscribe to every possible topic 16 | this.client.subscribe( MQTT.TOPICS.PERSON ); 17 | this.client.subscribe( MQTT.TOPICS.DURATION ); 18 | this.publish( "presence", "hello from react" ); 19 | 20 | console.log( "connected to " + MQTT.MQTT_SERVER ); 21 | } ); 22 | 23 | // listen for mqtt messages 24 | this.client.on( "message", ( topic, message ) => { 25 | /*console.log( topic, message );*/ 26 | let m = JSON.parse( message.toString() ); 27 | // call all registered handlers 28 | this.handlers.forEach( ( h ) => { 29 | h.func( topic, m ); 30 | } ); 31 | } ); 32 | } 33 | 34 | addHandler( id, handler ) { 35 | this.handlers.push( { id, func: handler } ); 36 | } 37 | 38 | removeHandler( id ) { 39 | this.handlers = this.handlers.filter( ( h ) => { 40 | return h.id === id ? false : true; 41 | } ); 42 | } 43 | 44 | publish( topic, payload ) { 45 | const p = payload || {}; 46 | this.client.publish( topic, JSON.stringify( p ) ); 47 | } 48 | } 49 | 50 | export default ( new MqttClient ); 51 | -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/IntelClear-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/IntelClear-Bold.eot -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/IntelClear-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/IntelClear-Bold.ttf -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/IntelClear-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/IntelClear-Bold.woff -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/IntelClear-BoldItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/IntelClear-BoldItalic.eot -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/IntelClear-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/IntelClear-BoldItalic.ttf -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/IntelClear-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/IntelClear-BoldItalic.woff -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/IntelClear-Italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/IntelClear-Italic.eot -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/IntelClear-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/IntelClear-Italic.ttf -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/IntelClear-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/IntelClear-Italic.woff -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/IntelClear-Light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/IntelClear-Light.eot -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/IntelClear-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/IntelClear-Light.ttf -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/IntelClear-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/IntelClear-Light.woff -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/IntelClear-LightItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/IntelClear-LightItalic.eot -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/IntelClear-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/IntelClear-LightItalic.ttf -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/IntelClear-LightItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/IntelClear-LightItalic.woff -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/IntelClear-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/IntelClear-Regular.eot -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/IntelClear-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/IntelClear-Regular.ttf -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/IntelClear-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/IntelClear-Regular.woff -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /webservice/ui/src/assets/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /webservice/ui/src/assets/images/Group.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /webservice/ui/src/assets/images/background-02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prateeksawhney97/People-Counter-Application-Using-Intel-OpenVINO-Toolkit/4bd99049efc0b02c4a446ebce9bc5b5a016ee79d/webservice/ui/src/assets/images/background-02.jpg -------------------------------------------------------------------------------- /webservice/ui/src/assets/images/intel-people-counter.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 11 | 12 | 14 | 19 | 24 | 29 | 31 | 36 | 41 | 46 | 49 | 52 | 54 | 59 | 61 | 64 | 65 | 66 | 67 | 68 | 69 | 72 | 73 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /webservice/ui/src/components/button/Button.css: -------------------------------------------------------------------------------- 1 | @import '../../constants/constants.css'; 2 | 3 | .btn { 4 | border: 0; 5 | outline: 0; 6 | width: 200px; 7 | height: 40px; 8 | text-align: center; 9 | background: var(--my-white); 10 | padding: 10px; 11 | cursor: pointer; 12 | font-size: 16px; 13 | box-sizing: border-box; 14 | margin: 10px; 15 | display: block; 16 | transition: color 100ms linear, background-color 100ms linear; 17 | 18 | &.active { 19 | background-color: var(--intel-azure); 20 | color: var(--my-white); 21 | } 22 | 23 | &.btn-contact { 24 | display: block; 25 | margin: 0 10px; 26 | padding: 0 0 0 10px; 27 | text-align: left; 28 | width: 90%; 29 | &:hover { 30 | background-color: var(--intel-azure); 31 | color: var(--my-white); 32 | } 33 | &:disabled{ 34 | opacity: 0.8; 35 | cursor: not-allowed; 36 | } 37 | } 38 | 39 | &.plus { text-align: left; 40 | &:hover { 41 | & span { 42 | color: var(--intel-azure); 43 | } 44 | } 45 | &:before { 46 | background-color: var(--my-white); 47 | color: var(--intel-azure); 48 | content:"\f055"; 49 | display: inline-block; 50 | font-family: 'FontAwesome'; 51 | font-size: 16px; 52 | margin-right: 5px; 53 | text-align: center; 54 | } 55 | } 56 | 57 | & * { 58 | cursor: pointer; 59 | } 60 | } 61 | 62 | button:disabled span { 63 | opacity: 0.8; 64 | cursor: not-allowed; 65 | } -------------------------------------------------------------------------------- /webservice/ui/src/components/button/Button.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import "./Button.css"; 4 | 5 | const Button = ( { label, click, type, disabled, data, addClass } ) => ( 6 | 9 | ); 10 | 11 | Button.propTypes = { 12 | label: PropTypes.string, 13 | type: PropTypes.string, 14 | disabled: PropTypes.bool, 15 | click: PropTypes.func, 16 | addClass: PropTypes.string, 17 | data: PropTypes.shape( { 18 | id: PropTypes.string, 19 | image: PropTypes.string, 20 | name: PropTypes.string, 21 | clearanceType: PropTypes.string, 22 | status: PropTypes.string, 23 | position: PropTypes.string, 24 | age: PropTypes.oneOfType( [ 25 | PropTypes.string, 26 | PropTypes.number ] ), 27 | height: PropTypes.string, 28 | weight: PropTypes.string, 29 | phone: PropTypes.string, 30 | email: PropTypes.string, 31 | } ), 32 | }; 33 | 34 | Button.defaultProps = { 35 | label: undefined, 36 | type: "button", 37 | disabled: false, 38 | click: undefined, 39 | addClass: "", 40 | data: undefined, 41 | }; 42 | 43 | export default Button; 44 | -------------------------------------------------------------------------------- /webservice/ui/src/components/button/Button.test.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { shallow } from "enzyme"; 3 | import Button from "./Button"; 4 | 5 | // mock function to use for prop 6 | const testClick = jest.fn(); 7 | 8 | // component instance to test 9 | const wrapper = shallow( 184 | 185 |
186 | 187 | 188 |
189 | 190 | ); 191 | } 192 | } 193 | 194 | Stats.propTypes = { 195 | statsOn: PropTypes.bool.isRequired, 196 | totalCountOn: PropTypes.bool.isRequired, 197 | toggleTotalCount: PropTypes.func.isRequired, 198 | }; 199 | 200 | Stats.defaultProps = { 201 | }; 202 | 203 | export default Stats; 204 | -------------------------------------------------------------------------------- /webservice/ui/src/font-awesome.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | /* FONT PATH 6 | * -------------------------- */ 7 | @font-face { 8 | font-family: 'FontAwesome'; 9 | src: url('assets/fonts/fontawesome-webfont.eot?v=4.7.0'); 10 | src: url('assets/fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('assets/fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('assets/fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('assets/fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('assets/fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg'); 11 | font-weight: normal; 12 | font-style: normal; 13 | } 14 | .fa { 15 | display: inline-block; 16 | font: normal normal normal 14px/1 FontAwesome; 17 | font-size: inherit; 18 | text-rendering: auto; 19 | -webkit-font-smoothing: antialiased; 20 | -moz-osx-font-smoothing: grayscale; 21 | } 22 | /* makes the font 33% larger relative to the icon container */ 23 | .fa-lg { 24 | font-size: 1.33333333em; 25 | line-height: 0.75em; 26 | vertical-align: -15%; 27 | } 28 | .fa-2x { 29 | font-size: 2em; 30 | } 31 | .fa-3x { 32 | font-size: 3em; 33 | } 34 | .fa-4x { 35 | font-size: 4em; 36 | } 37 | .fa-5x { 38 | font-size: 5em; 39 | } 40 | .fa-fw { 41 | width: 1.28571429em; 42 | text-align: center; 43 | } 44 | .fa-ul { 45 | padding-left: 0; 46 | margin-left: 2.14285714em; 47 | list-style-type: none; 48 | } 49 | .fa-ul > li { 50 | position: relative; 51 | } 52 | .fa-li { 53 | position: absolute; 54 | left: -2.14285714em; 55 | width: 2.14285714em; 56 | top: 0.14285714em; 57 | text-align: center; 58 | } 59 | .fa-li.fa-lg { 60 | left: -1.85714286em; 61 | } 62 | .fa-border { 63 | padding: .2em .25em .15em; 64 | border: solid 0.08em #eeeeee; 65 | border-radius: .1em; 66 | } 67 | .fa-pull-left { 68 | float: left; 69 | } 70 | .fa-pull-right { 71 | float: right; 72 | } 73 | .fa.fa-pull-left { 74 | margin-right: .3em; 75 | } 76 | .fa.fa-pull-right { 77 | margin-left: .3em; 78 | } 79 | /* Deprecated as of 4.4.0 */ 80 | .pull-right { 81 | float: right; 82 | } 83 | .pull-left { 84 | float: left; 85 | } 86 | .fa.pull-left { 87 | margin-right: .3em; 88 | } 89 | .fa.pull-right { 90 | margin-left: .3em; 91 | } 92 | .fa-spin { 93 | -webkit-animation: fa-spin 2s infinite linear; 94 | animation: fa-spin 2s infinite linear; 95 | } 96 | .fa-pulse { 97 | -webkit-animation: fa-spin 1s infinite steps(8); 98 | animation: fa-spin 1s infinite steps(8); 99 | } 100 | @-webkit-keyframes fa-spin { 101 | 0% { 102 | -webkit-transform: rotate(0deg); 103 | transform: rotate(0deg); 104 | } 105 | 100% { 106 | -webkit-transform: rotate(359deg); 107 | transform: rotate(359deg); 108 | } 109 | } 110 | @keyframes fa-spin { 111 | 0% { 112 | -webkit-transform: rotate(0deg); 113 | transform: rotate(0deg); 114 | } 115 | 100% { 116 | -webkit-transform: rotate(359deg); 117 | transform: rotate(359deg); 118 | } 119 | } 120 | .fa-rotate-90 { 121 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)"; 122 | -webkit-transform: rotate(90deg); 123 | -ms-transform: rotate(90deg); 124 | transform: rotate(90deg); 125 | } 126 | .fa-rotate-180 { 127 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)"; 128 | -webkit-transform: rotate(180deg); 129 | -ms-transform: rotate(180deg); 130 | transform: rotate(180deg); 131 | } 132 | .fa-rotate-270 { 133 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)"; 134 | -webkit-transform: rotate(270deg); 135 | -ms-transform: rotate(270deg); 136 | transform: rotate(270deg); 137 | } 138 | .fa-flip-horizontal { 139 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)"; 140 | -webkit-transform: scale(-1, 1); 141 | -ms-transform: scale(-1, 1); 142 | transform: scale(-1, 1); 143 | } 144 | .fa-flip-vertical { 145 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"; 146 | -webkit-transform: scale(1, -1); 147 | -ms-transform: scale(1, -1); 148 | transform: scale(1, -1); 149 | } 150 | :root .fa-rotate-90, 151 | :root .fa-rotate-180, 152 | :root .fa-rotate-270, 153 | :root .fa-flip-horizontal, 154 | :root .fa-flip-vertical { 155 | filter: none; 156 | } 157 | .fa-stack { 158 | position: relative; 159 | display: inline-block; 160 | width: 2em; 161 | height: 2em; 162 | line-height: 2em; 163 | vertical-align: middle; 164 | } 165 | .fa-stack-1x, 166 | .fa-stack-2x { 167 | position: absolute; 168 | left: 0; 169 | width: 100%; 170 | text-align: center; 171 | } 172 | .fa-stack-1x { 173 | line-height: inherit; 174 | } 175 | .fa-stack-2x { 176 | font-size: 2em; 177 | } 178 | .fa-inverse { 179 | color: #ffffff; 180 | } 181 | /* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen 182 | readers do not read off random characters that represent icons */ 183 | .fa-glass:before { 184 | content: "\f000"; 185 | } 186 | .fa-music:before { 187 | content: "\f001"; 188 | } 189 | .fa-search:before { 190 | content: "\f002"; 191 | } 192 | .fa-envelope-o:before { 193 | content: "\f003"; 194 | } 195 | .fa-heart:before { 196 | content: "\f004"; 197 | } 198 | .fa-star:before { 199 | content: "\f005"; 200 | } 201 | .fa-star-o:before { 202 | content: "\f006"; 203 | } 204 | .fa-user:before { 205 | content: "\f007"; 206 | } 207 | .fa-film:before { 208 | content: "\f008"; 209 | } 210 | .fa-th-large:before { 211 | content: "\f009"; 212 | } 213 | .fa-th:before { 214 | content: "\f00a"; 215 | } 216 | .fa-th-list:before { 217 | content: "\f00b"; 218 | } 219 | .fa-check:before { 220 | content: "\f00c"; 221 | } 222 | .fa-remove:before, 223 | .fa-close:before, 224 | .fa-times:before { 225 | content: "\f00d"; 226 | } 227 | .fa-search-plus:before { 228 | content: "\f00e"; 229 | } 230 | .fa-search-minus:before { 231 | content: "\f010"; 232 | } 233 | .fa-power-off:before { 234 | content: "\f011"; 235 | } 236 | .fa-signal:before { 237 | content: "\f012"; 238 | } 239 | .fa-gear:before, 240 | .fa-cog:before { 241 | content: "\f013"; 242 | } 243 | .fa-trash-o:before { 244 | content: "\f014"; 245 | } 246 | .fa-home:before { 247 | content: "\f015"; 248 | } 249 | .fa-file-o:before { 250 | content: "\f016"; 251 | } 252 | .fa-clock-o:before { 253 | content: "\f017"; 254 | } 255 | .fa-road:before { 256 | content: "\f018"; 257 | } 258 | .fa-download:before { 259 | content: "\f019"; 260 | } 261 | .fa-arrow-circle-o-down:before { 262 | content: "\f01a"; 263 | } 264 | .fa-arrow-circle-o-up:before { 265 | content: "\f01b"; 266 | } 267 | .fa-inbox:before { 268 | content: "\f01c"; 269 | } 270 | .fa-play-circle-o:before { 271 | content: "\f01d"; 272 | } 273 | .fa-rotate-right:before, 274 | .fa-repeat:before { 275 | content: "\f01e"; 276 | } 277 | .fa-refresh:before { 278 | content: "\f021"; 279 | } 280 | .fa-list-alt:before { 281 | content: "\f022"; 282 | } 283 | .fa-lock:before { 284 | content: "\f023"; 285 | } 286 | .fa-flag:before { 287 | content: "\f024"; 288 | } 289 | .fa-headphones:before { 290 | content: "\f025"; 291 | } 292 | .fa-volume-off:before { 293 | content: "\f026"; 294 | } 295 | .fa-volume-down:before { 296 | content: "\f027"; 297 | } 298 | .fa-volume-up:before { 299 | content: "\f028"; 300 | } 301 | .fa-qrcode:before { 302 | content: "\f029"; 303 | } 304 | .fa-barcode:before { 305 | content: "\f02a"; 306 | } 307 | .fa-tag:before { 308 | content: "\f02b"; 309 | } 310 | .fa-tags:before { 311 | content: "\f02c"; 312 | } 313 | .fa-book:before { 314 | content: "\f02d"; 315 | } 316 | .fa-bookmark:before { 317 | content: "\f02e"; 318 | } 319 | .fa-print:before { 320 | content: "\f02f"; 321 | } 322 | .fa-camera:before { 323 | content: "\f030"; 324 | } 325 | .fa-font:before { 326 | content: "\f031"; 327 | } 328 | .fa-bold:before { 329 | content: "\f032"; 330 | } 331 | .fa-italic:before { 332 | content: "\f033"; 333 | } 334 | .fa-text-height:before { 335 | content: "\f034"; 336 | } 337 | .fa-text-width:before { 338 | content: "\f035"; 339 | } 340 | .fa-align-left:before { 341 | content: "\f036"; 342 | } 343 | .fa-align-center:before { 344 | content: "\f037"; 345 | } 346 | .fa-align-right:before { 347 | content: "\f038"; 348 | } 349 | .fa-align-justify:before { 350 | content: "\f039"; 351 | } 352 | .fa-list:before { 353 | content: "\f03a"; 354 | } 355 | .fa-dedent:before, 356 | .fa-outdent:before { 357 | content: "\f03b"; 358 | } 359 | .fa-indent:before { 360 | content: "\f03c"; 361 | } 362 | .fa-video-camera:before { 363 | content: "\f03d"; 364 | } 365 | .fa-photo:before, 366 | .fa-image:before, 367 | .fa-picture-o:before { 368 | content: "\f03e"; 369 | } 370 | .fa-pencil:before { 371 | content: "\f040"; 372 | } 373 | .fa-map-marker:before { 374 | content: "\f041"; 375 | } 376 | .fa-adjust:before { 377 | content: "\f042"; 378 | } 379 | .fa-tint:before { 380 | content: "\f043"; 381 | } 382 | .fa-edit:before, 383 | .fa-pencil-square-o:before { 384 | content: "\f044"; 385 | } 386 | .fa-share-square-o:before { 387 | content: "\f045"; 388 | } 389 | .fa-check-square-o:before { 390 | content: "\f046"; 391 | } 392 | .fa-arrows:before { 393 | content: "\f047"; 394 | } 395 | .fa-step-backward:before { 396 | content: "\f048"; 397 | } 398 | .fa-fast-backward:before { 399 | content: "\f049"; 400 | } 401 | .fa-backward:before { 402 | content: "\f04a"; 403 | } 404 | .fa-play:before { 405 | content: "\f04b"; 406 | } 407 | .fa-pause:before { 408 | content: "\f04c"; 409 | } 410 | .fa-stop:before { 411 | content: "\f04d"; 412 | } 413 | .fa-forward:before { 414 | content: "\f04e"; 415 | } 416 | .fa-fast-forward:before { 417 | content: "\f050"; 418 | } 419 | .fa-step-forward:before { 420 | content: "\f051"; 421 | } 422 | .fa-eject:before { 423 | content: "\f052"; 424 | } 425 | .fa-chevron-left:before { 426 | content: "\f053"; 427 | } 428 | .fa-chevron-right:before { 429 | content: "\f054"; 430 | } 431 | .fa-plus-circle:before { 432 | content: "\f055"; 433 | } 434 | .fa-minus-circle:before { 435 | content: "\f056"; 436 | } 437 | .fa-times-circle:before { 438 | content: "\f057"; 439 | } 440 | .fa-check-circle:before { 441 | content: "\f058"; 442 | } 443 | .fa-question-circle:before { 444 | content: "\f059"; 445 | } 446 | .fa-info-circle:before { 447 | content: "\f05a"; 448 | } 449 | .fa-crosshairs:before { 450 | content: "\f05b"; 451 | } 452 | .fa-times-circle-o:before { 453 | content: "\f05c"; 454 | } 455 | .fa-check-circle-o:before { 456 | content: "\f05d"; 457 | } 458 | .fa-ban:before { 459 | content: "\f05e"; 460 | } 461 | .fa-arrow-left:before { 462 | content: "\f060"; 463 | } 464 | .fa-arrow-right:before { 465 | content: "\f061"; 466 | } 467 | .fa-arrow-up:before { 468 | content: "\f062"; 469 | } 470 | .fa-arrow-down:before { 471 | content: "\f063"; 472 | } 473 | .fa-mail-forward:before, 474 | .fa-share:before { 475 | content: "\f064"; 476 | } 477 | .fa-expand:before { 478 | content: "\f065"; 479 | } 480 | .fa-compress:before { 481 | content: "\f066"; 482 | } 483 | .fa-plus:before { 484 | content: "\f067"; 485 | } 486 | .fa-minus:before { 487 | content: "\f068"; 488 | } 489 | .fa-asterisk:before { 490 | content: "\f069"; 491 | } 492 | .fa-exclamation-circle:before { 493 | content: "\f06a"; 494 | } 495 | .fa-gift:before { 496 | content: "\f06b"; 497 | } 498 | .fa-leaf:before { 499 | content: "\f06c"; 500 | } 501 | .fa-fire:before { 502 | content: "\f06d"; 503 | } 504 | .fa-eye:before { 505 | content: "\f06e"; 506 | } 507 | .fa-eye-slash:before { 508 | content: "\f070"; 509 | } 510 | .fa-warning:before, 511 | .fa-exclamation-triangle:before { 512 | content: "\f071"; 513 | } 514 | .fa-plane:before { 515 | content: "\f072"; 516 | } 517 | .fa-calendar:before { 518 | content: "\f073"; 519 | } 520 | .fa-random:before { 521 | content: "\f074"; 522 | } 523 | .fa-comment:before { 524 | content: "\f075"; 525 | } 526 | .fa-magnet:before { 527 | content: "\f076"; 528 | } 529 | .fa-chevron-up:before { 530 | content: "\f077"; 531 | } 532 | .fa-chevron-down:before { 533 | content: "\f078"; 534 | } 535 | .fa-retweet:before { 536 | content: "\f079"; 537 | } 538 | .fa-shopping-cart:before { 539 | content: "\f07a"; 540 | } 541 | .fa-folder:before { 542 | content: "\f07b"; 543 | } 544 | .fa-folder-open:before { 545 | content: "\f07c"; 546 | } 547 | .fa-arrows-v:before { 548 | content: "\f07d"; 549 | } 550 | .fa-arrows-h:before { 551 | content: "\f07e"; 552 | } 553 | .fa-bar-chart-o:before, 554 | .fa-bar-chart:before { 555 | content: "\f080"; 556 | } 557 | .fa-twitter-square:before { 558 | content: "\f081"; 559 | } 560 | .fa-facebook-square:before { 561 | content: "\f082"; 562 | } 563 | .fa-camera-retro:before { 564 | content: "\f083"; 565 | } 566 | .fa-key:before { 567 | content: "\f084"; 568 | } 569 | .fa-gears:before, 570 | .fa-cogs:before { 571 | content: "\f085"; 572 | } 573 | .fa-comments:before { 574 | content: "\f086"; 575 | } 576 | .fa-thumbs-o-up:before { 577 | content: "\f087"; 578 | } 579 | .fa-thumbs-o-down:before { 580 | content: "\f088"; 581 | } 582 | .fa-star-half:before { 583 | content: "\f089"; 584 | } 585 | .fa-heart-o:before { 586 | content: "\f08a"; 587 | } 588 | .fa-sign-out:before { 589 | content: "\f08b"; 590 | } 591 | .fa-linkedin-square:before { 592 | content: "\f08c"; 593 | } 594 | .fa-thumb-tack:before { 595 | content: "\f08d"; 596 | } 597 | .fa-external-link:before { 598 | content: "\f08e"; 599 | } 600 | .fa-sign-in:before { 601 | content: "\f090"; 602 | } 603 | .fa-trophy:before { 604 | content: "\f091"; 605 | } 606 | .fa-github-square:before { 607 | content: "\f092"; 608 | } 609 | .fa-upload:before { 610 | content: "\f093"; 611 | } 612 | .fa-lemon-o:before { 613 | content: "\f094"; 614 | } 615 | .fa-phone:before { 616 | content: "\f095"; 617 | } 618 | .fa-square-o:before { 619 | content: "\f096"; 620 | } 621 | .fa-bookmark-o:before { 622 | content: "\f097"; 623 | } 624 | .fa-phone-square:before { 625 | content: "\f098"; 626 | } 627 | .fa-twitter:before { 628 | content: "\f099"; 629 | } 630 | .fa-facebook-f:before, 631 | .fa-facebook:before { 632 | content: "\f09a"; 633 | } 634 | .fa-github:before { 635 | content: "\f09b"; 636 | } 637 | .fa-unlock:before { 638 | content: "\f09c"; 639 | } 640 | .fa-credit-card:before { 641 | content: "\f09d"; 642 | } 643 | .fa-feed:before, 644 | .fa-rss:before { 645 | content: "\f09e"; 646 | } 647 | .fa-hdd-o:before { 648 | content: "\f0a0"; 649 | } 650 | .fa-bullhorn:before { 651 | content: "\f0a1"; 652 | } 653 | .fa-bell:before { 654 | content: "\f0f3"; 655 | } 656 | .fa-certificate:before { 657 | content: "\f0a3"; 658 | } 659 | .fa-hand-o-right:before { 660 | content: "\f0a4"; 661 | } 662 | .fa-hand-o-left:before { 663 | content: "\f0a5"; 664 | } 665 | .fa-hand-o-up:before { 666 | content: "\f0a6"; 667 | } 668 | .fa-hand-o-down:before { 669 | content: "\f0a7"; 670 | } 671 | .fa-arrow-circle-left:before { 672 | content: "\f0a8"; 673 | } 674 | .fa-arrow-circle-right:before { 675 | content: "\f0a9"; 676 | } 677 | .fa-arrow-circle-up:before { 678 | content: "\f0aa"; 679 | } 680 | .fa-arrow-circle-down:before { 681 | content: "\f0ab"; 682 | } 683 | .fa-globe:before { 684 | content: "\f0ac"; 685 | } 686 | .fa-wrench:before { 687 | content: "\f0ad"; 688 | } 689 | .fa-tasks:before { 690 | content: "\f0ae"; 691 | } 692 | .fa-filter:before { 693 | content: "\f0b0"; 694 | } 695 | .fa-briefcase:before { 696 | content: "\f0b1"; 697 | } 698 | .fa-arrows-alt:before { 699 | content: "\f0b2"; 700 | } 701 | .fa-group:before, 702 | .fa-users:before { 703 | content: "\f0c0"; 704 | } 705 | .fa-chain:before, 706 | .fa-link:before { 707 | content: "\f0c1"; 708 | } 709 | .fa-cloud:before { 710 | content: "\f0c2"; 711 | } 712 | .fa-flask:before { 713 | content: "\f0c3"; 714 | } 715 | .fa-cut:before, 716 | .fa-scissors:before { 717 | content: "\f0c4"; 718 | } 719 | .fa-copy:before, 720 | .fa-files-o:before { 721 | content: "\f0c5"; 722 | } 723 | .fa-paperclip:before { 724 | content: "\f0c6"; 725 | } 726 | .fa-save:before, 727 | .fa-floppy-o:before { 728 | content: "\f0c7"; 729 | } 730 | .fa-square:before { 731 | content: "\f0c8"; 732 | } 733 | .fa-navicon:before, 734 | .fa-reorder:before, 735 | .fa-bars:before { 736 | content: "\f0c9"; 737 | } 738 | .fa-list-ul:before { 739 | content: "\f0ca"; 740 | } 741 | .fa-list-ol:before { 742 | content: "\f0cb"; 743 | } 744 | .fa-strikethrough:before { 745 | content: "\f0cc"; 746 | } 747 | .fa-underline:before { 748 | content: "\f0cd"; 749 | } 750 | .fa-table:before { 751 | content: "\f0ce"; 752 | } 753 | .fa-magic:before { 754 | content: "\f0d0"; 755 | } 756 | .fa-truck:before { 757 | content: "\f0d1"; 758 | } 759 | .fa-pinterest:before { 760 | content: "\f0d2"; 761 | } 762 | .fa-pinterest-square:before { 763 | content: "\f0d3"; 764 | } 765 | .fa-google-plus-square:before { 766 | content: "\f0d4"; 767 | } 768 | .fa-google-plus:before { 769 | content: "\f0d5"; 770 | } 771 | .fa-money:before { 772 | content: "\f0d6"; 773 | } 774 | .fa-caret-down:before { 775 | content: "\f0d7"; 776 | } 777 | .fa-caret-up:before { 778 | content: "\f0d8"; 779 | } 780 | .fa-caret-left:before { 781 | content: "\f0d9"; 782 | } 783 | .fa-caret-right:before { 784 | content: "\f0da"; 785 | } 786 | .fa-columns:before { 787 | content: "\f0db"; 788 | } 789 | .fa-unsorted:before, 790 | .fa-sort:before { 791 | content: "\f0dc"; 792 | } 793 | .fa-sort-down:before, 794 | .fa-sort-desc:before { 795 | content: "\f0dd"; 796 | } 797 | .fa-sort-up:before, 798 | .fa-sort-asc:before { 799 | content: "\f0de"; 800 | } 801 | .fa-envelope:before { 802 | content: "\f0e0"; 803 | } 804 | .fa-linkedin:before { 805 | content: "\f0e1"; 806 | } 807 | .fa-rotate-left:before, 808 | .fa-undo:before { 809 | content: "\f0e2"; 810 | } 811 | .fa-legal:before, 812 | .fa-gavel:before { 813 | content: "\f0e3"; 814 | } 815 | .fa-dashboard:before, 816 | .fa-tachometer:before { 817 | content: "\f0e4"; 818 | } 819 | .fa-comment-o:before { 820 | content: "\f0e5"; 821 | } 822 | .fa-comments-o:before { 823 | content: "\f0e6"; 824 | } 825 | .fa-flash:before, 826 | .fa-bolt:before { 827 | content: "\f0e7"; 828 | } 829 | .fa-sitemap:before { 830 | content: "\f0e8"; 831 | } 832 | .fa-umbrella:before { 833 | content: "\f0e9"; 834 | } 835 | .fa-paste:before, 836 | .fa-clipboard:before { 837 | content: "\f0ea"; 838 | } 839 | .fa-lightbulb-o:before { 840 | content: "\f0eb"; 841 | } 842 | .fa-exchange:before { 843 | content: "\f0ec"; 844 | } 845 | .fa-cloud-download:before { 846 | content: "\f0ed"; 847 | } 848 | .fa-cloud-upload:before { 849 | content: "\f0ee"; 850 | } 851 | .fa-user-md:before { 852 | content: "\f0f0"; 853 | } 854 | .fa-stethoscope:before { 855 | content: "\f0f1"; 856 | } 857 | .fa-suitcase:before { 858 | content: "\f0f2"; 859 | } 860 | .fa-bell-o:before { 861 | content: "\f0a2"; 862 | } 863 | .fa-coffee:before { 864 | content: "\f0f4"; 865 | } 866 | .fa-cutlery:before { 867 | content: "\f0f5"; 868 | } 869 | .fa-file-text-o:before { 870 | content: "\f0f6"; 871 | } 872 | .fa-building-o:before { 873 | content: "\f0f7"; 874 | } 875 | .fa-hospital-o:before { 876 | content: "\f0f8"; 877 | } 878 | .fa-ambulance:before { 879 | content: "\f0f9"; 880 | } 881 | .fa-medkit:before { 882 | content: "\f0fa"; 883 | } 884 | .fa-fighter-jet:before { 885 | content: "\f0fb"; 886 | } 887 | .fa-beer:before { 888 | content: "\f0fc"; 889 | } 890 | .fa-h-square:before { 891 | content: "\f0fd"; 892 | } 893 | .fa-plus-square:before { 894 | content: "\f0fe"; 895 | } 896 | .fa-angle-double-left:before { 897 | content: "\f100"; 898 | } 899 | .fa-angle-double-right:before { 900 | content: "\f101"; 901 | } 902 | .fa-angle-double-up:before { 903 | content: "\f102"; 904 | } 905 | .fa-angle-double-down:before { 906 | content: "\f103"; 907 | } 908 | .fa-angle-left:before { 909 | content: "\f104"; 910 | } 911 | .fa-angle-right:before { 912 | content: "\f105"; 913 | } 914 | .fa-angle-up:before { 915 | content: "\f106"; 916 | } 917 | .fa-angle-down:before { 918 | content: "\f107"; 919 | } 920 | .fa-desktop:before { 921 | content: "\f108"; 922 | } 923 | .fa-laptop:before { 924 | content: "\f109"; 925 | } 926 | .fa-tablet:before { 927 | content: "\f10a"; 928 | } 929 | .fa-mobile-phone:before, 930 | .fa-mobile:before { 931 | content: "\f10b"; 932 | } 933 | .fa-circle-o:before { 934 | content: "\f10c"; 935 | } 936 | .fa-quote-left:before { 937 | content: "\f10d"; 938 | } 939 | .fa-quote-right:before { 940 | content: "\f10e"; 941 | } 942 | .fa-spinner:before { 943 | content: "\f110"; 944 | } 945 | .fa-circle:before { 946 | content: "\f111"; 947 | } 948 | .fa-mail-reply:before, 949 | .fa-reply:before { 950 | content: "\f112"; 951 | } 952 | .fa-github-alt:before { 953 | content: "\f113"; 954 | } 955 | .fa-folder-o:before { 956 | content: "\f114"; 957 | } 958 | .fa-folder-open-o:before { 959 | content: "\f115"; 960 | } 961 | .fa-smile-o:before { 962 | content: "\f118"; 963 | } 964 | .fa-frown-o:before { 965 | content: "\f119"; 966 | } 967 | .fa-meh-o:before { 968 | content: "\f11a"; 969 | } 970 | .fa-gamepad:before { 971 | content: "\f11b"; 972 | } 973 | .fa-keyboard-o:before { 974 | content: "\f11c"; 975 | } 976 | .fa-flag-o:before { 977 | content: "\f11d"; 978 | } 979 | .fa-flag-checkered:before { 980 | content: "\f11e"; 981 | } 982 | .fa-terminal:before { 983 | content: "\f120"; 984 | } 985 | .fa-code:before { 986 | content: "\f121"; 987 | } 988 | .fa-mail-reply-all:before, 989 | .fa-reply-all:before { 990 | content: "\f122"; 991 | } 992 | .fa-star-half-empty:before, 993 | .fa-star-half-full:before, 994 | .fa-star-half-o:before { 995 | content: "\f123"; 996 | } 997 | .fa-location-arrow:before { 998 | content: "\f124"; 999 | } 1000 | .fa-crop:before { 1001 | content: "\f125"; 1002 | } 1003 | .fa-code-fork:before { 1004 | content: "\f126"; 1005 | } 1006 | .fa-unlink:before, 1007 | .fa-chain-broken:before { 1008 | content: "\f127"; 1009 | } 1010 | .fa-question:before { 1011 | content: "\f128"; 1012 | } 1013 | .fa-info:before { 1014 | content: "\f129"; 1015 | } 1016 | .fa-exclamation:before { 1017 | content: "\f12a"; 1018 | } 1019 | .fa-superscript:before { 1020 | content: "\f12b"; 1021 | } 1022 | .fa-subscript:before { 1023 | content: "\f12c"; 1024 | } 1025 | .fa-eraser:before { 1026 | content: "\f12d"; 1027 | } 1028 | .fa-puzzle-piece:before { 1029 | content: "\f12e"; 1030 | } 1031 | .fa-microphone:before { 1032 | content: "\f130"; 1033 | } 1034 | .fa-microphone-slash:before { 1035 | content: "\f131"; 1036 | } 1037 | .fa-shield:before { 1038 | content: "\f132"; 1039 | } 1040 | .fa-calendar-o:before { 1041 | content: "\f133"; 1042 | } 1043 | .fa-fire-extinguisher:before { 1044 | content: "\f134"; 1045 | } 1046 | .fa-rocket:before { 1047 | content: "\f135"; 1048 | } 1049 | .fa-maxcdn:before { 1050 | content: "\f136"; 1051 | } 1052 | .fa-chevron-circle-left:before { 1053 | content: "\f137"; 1054 | } 1055 | .fa-chevron-circle-right:before { 1056 | content: "\f138"; 1057 | } 1058 | .fa-chevron-circle-up:before { 1059 | content: "\f139"; 1060 | } 1061 | .fa-chevron-circle-down:before { 1062 | content: "\f13a"; 1063 | } 1064 | .fa-html5:before { 1065 | content: "\f13b"; 1066 | } 1067 | .fa-css3:before { 1068 | content: "\f13c"; 1069 | } 1070 | .fa-anchor:before { 1071 | content: "\f13d"; 1072 | } 1073 | .fa-unlock-alt:before { 1074 | content: "\f13e"; 1075 | } 1076 | .fa-bullseye:before { 1077 | content: "\f140"; 1078 | } 1079 | .fa-ellipsis-h:before { 1080 | content: "\f141"; 1081 | } 1082 | .fa-ellipsis-v:before { 1083 | content: "\f142"; 1084 | } 1085 | .fa-rss-square:before { 1086 | content: "\f143"; 1087 | } 1088 | .fa-play-circle:before { 1089 | content: "\f144"; 1090 | } 1091 | .fa-ticket:before { 1092 | content: "\f145"; 1093 | } 1094 | .fa-minus-square:before { 1095 | content: "\f146"; 1096 | } 1097 | .fa-minus-square-o:before { 1098 | content: "\f147"; 1099 | } 1100 | .fa-level-up:before { 1101 | content: "\f148"; 1102 | } 1103 | .fa-level-down:before { 1104 | content: "\f149"; 1105 | } 1106 | .fa-check-square:before { 1107 | content: "\f14a"; 1108 | } 1109 | .fa-pencil-square:before { 1110 | content: "\f14b"; 1111 | } 1112 | .fa-external-link-square:before { 1113 | content: "\f14c"; 1114 | } 1115 | .fa-share-square:before { 1116 | content: "\f14d"; 1117 | } 1118 | .fa-compass:before { 1119 | content: "\f14e"; 1120 | } 1121 | .fa-toggle-down:before, 1122 | .fa-caret-square-o-down:before { 1123 | content: "\f150"; 1124 | } 1125 | .fa-toggle-up:before, 1126 | .fa-caret-square-o-up:before { 1127 | content: "\f151"; 1128 | } 1129 | .fa-toggle-right:before, 1130 | .fa-caret-square-o-right:before { 1131 | content: "\f152"; 1132 | } 1133 | .fa-euro:before, 1134 | .fa-eur:before { 1135 | content: "\f153"; 1136 | } 1137 | .fa-gbp:before { 1138 | content: "\f154"; 1139 | } 1140 | .fa-dollar:before, 1141 | .fa-usd:before { 1142 | content: "\f155"; 1143 | } 1144 | .fa-rupee:before, 1145 | .fa-inr:before { 1146 | content: "\f156"; 1147 | } 1148 | .fa-cny:before, 1149 | .fa-rmb:before, 1150 | .fa-yen:before, 1151 | .fa-jpy:before { 1152 | content: "\f157"; 1153 | } 1154 | .fa-ruble:before, 1155 | .fa-rouble:before, 1156 | .fa-rub:before { 1157 | content: "\f158"; 1158 | } 1159 | .fa-won:before, 1160 | .fa-krw:before { 1161 | content: "\f159"; 1162 | } 1163 | .fa-bitcoin:before, 1164 | .fa-btc:before { 1165 | content: "\f15a"; 1166 | } 1167 | .fa-file:before { 1168 | content: "\f15b"; 1169 | } 1170 | .fa-file-text:before { 1171 | content: "\f15c"; 1172 | } 1173 | .fa-sort-alpha-asc:before { 1174 | content: "\f15d"; 1175 | } 1176 | .fa-sort-alpha-desc:before { 1177 | content: "\f15e"; 1178 | } 1179 | .fa-sort-amount-asc:before { 1180 | content: "\f160"; 1181 | } 1182 | .fa-sort-amount-desc:before { 1183 | content: "\f161"; 1184 | } 1185 | .fa-sort-numeric-asc:before { 1186 | content: "\f162"; 1187 | } 1188 | .fa-sort-numeric-desc:before { 1189 | content: "\f163"; 1190 | } 1191 | .fa-thumbs-up:before { 1192 | content: "\f164"; 1193 | } 1194 | .fa-thumbs-down:before { 1195 | content: "\f165"; 1196 | } 1197 | .fa-youtube-square:before { 1198 | content: "\f166"; 1199 | } 1200 | .fa-youtube:before { 1201 | content: "\f167"; 1202 | } 1203 | .fa-xing:before { 1204 | content: "\f168"; 1205 | } 1206 | .fa-xing-square:before { 1207 | content: "\f169"; 1208 | } 1209 | .fa-youtube-play:before { 1210 | content: "\f16a"; 1211 | } 1212 | .fa-dropbox:before { 1213 | content: "\f16b"; 1214 | } 1215 | .fa-stack-overflow:before { 1216 | content: "\f16c"; 1217 | } 1218 | .fa-instagram:before { 1219 | content: "\f16d"; 1220 | } 1221 | .fa-flickr:before { 1222 | content: "\f16e"; 1223 | } 1224 | .fa-adn:before { 1225 | content: "\f170"; 1226 | } 1227 | .fa-bitbucket:before { 1228 | content: "\f171"; 1229 | } 1230 | .fa-bitbucket-square:before { 1231 | content: "\f172"; 1232 | } 1233 | .fa-tumblr:before { 1234 | content: "\f173"; 1235 | } 1236 | .fa-tumblr-square:before { 1237 | content: "\f174"; 1238 | } 1239 | .fa-long-arrow-down:before { 1240 | content: "\f175"; 1241 | } 1242 | .fa-long-arrow-up:before { 1243 | content: "\f176"; 1244 | } 1245 | .fa-long-arrow-left:before { 1246 | content: "\f177"; 1247 | } 1248 | .fa-long-arrow-right:before { 1249 | content: "\f178"; 1250 | } 1251 | .fa-apple:before { 1252 | content: "\f179"; 1253 | } 1254 | .fa-windows:before { 1255 | content: "\f17a"; 1256 | } 1257 | .fa-android:before { 1258 | content: "\f17b"; 1259 | } 1260 | .fa-linux:before { 1261 | content: "\f17c"; 1262 | } 1263 | .fa-dribbble:before { 1264 | content: "\f17d"; 1265 | } 1266 | .fa-skype:before { 1267 | content: "\f17e"; 1268 | } 1269 | .fa-foursquare:before { 1270 | content: "\f180"; 1271 | } 1272 | .fa-trello:before { 1273 | content: "\f181"; 1274 | } 1275 | .fa-female:before { 1276 | content: "\f182"; 1277 | } 1278 | .fa-male:before { 1279 | content: "\f183"; 1280 | } 1281 | .fa-gittip:before, 1282 | .fa-gratipay:before { 1283 | content: "\f184"; 1284 | } 1285 | .fa-sun-o:before { 1286 | content: "\f185"; 1287 | } 1288 | .fa-moon-o:before { 1289 | content: "\f186"; 1290 | } 1291 | .fa-archive:before { 1292 | content: "\f187"; 1293 | } 1294 | .fa-bug:before { 1295 | content: "\f188"; 1296 | } 1297 | .fa-vk:before { 1298 | content: "\f189"; 1299 | } 1300 | .fa-weibo:before { 1301 | content: "\f18a"; 1302 | } 1303 | .fa-renren:before { 1304 | content: "\f18b"; 1305 | } 1306 | .fa-pagelines:before { 1307 | content: "\f18c"; 1308 | } 1309 | .fa-stack-exchange:before { 1310 | content: "\f18d"; 1311 | } 1312 | .fa-arrow-circle-o-right:before { 1313 | content: "\f18e"; 1314 | } 1315 | .fa-arrow-circle-o-left:before { 1316 | content: "\f190"; 1317 | } 1318 | .fa-toggle-left:before, 1319 | .fa-caret-square-o-left:before { 1320 | content: "\f191"; 1321 | } 1322 | .fa-dot-circle-o:before { 1323 | content: "\f192"; 1324 | } 1325 | .fa-wheelchair:before { 1326 | content: "\f193"; 1327 | } 1328 | .fa-vimeo-square:before { 1329 | content: "\f194"; 1330 | } 1331 | .fa-turkish-lira:before, 1332 | .fa-try:before { 1333 | content: "\f195"; 1334 | } 1335 | .fa-plus-square-o:before { 1336 | content: "\f196"; 1337 | } 1338 | .fa-space-shuttle:before { 1339 | content: "\f197"; 1340 | } 1341 | .fa-slack:before { 1342 | content: "\f198"; 1343 | } 1344 | .fa-envelope-square:before { 1345 | content: "\f199"; 1346 | } 1347 | .fa-wordpress:before { 1348 | content: "\f19a"; 1349 | } 1350 | .fa-openid:before { 1351 | content: "\f19b"; 1352 | } 1353 | .fa-institution:before, 1354 | .fa-bank:before, 1355 | .fa-university:before { 1356 | content: "\f19c"; 1357 | } 1358 | .fa-mortar-board:before, 1359 | .fa-graduation-cap:before { 1360 | content: "\f19d"; 1361 | } 1362 | .fa-yahoo:before { 1363 | content: "\f19e"; 1364 | } 1365 | .fa-google:before { 1366 | content: "\f1a0"; 1367 | } 1368 | .fa-reddit:before { 1369 | content: "\f1a1"; 1370 | } 1371 | .fa-reddit-square:before { 1372 | content: "\f1a2"; 1373 | } 1374 | .fa-stumbleupon-circle:before { 1375 | content: "\f1a3"; 1376 | } 1377 | .fa-stumbleupon:before { 1378 | content: "\f1a4"; 1379 | } 1380 | .fa-delicious:before { 1381 | content: "\f1a5"; 1382 | } 1383 | .fa-digg:before { 1384 | content: "\f1a6"; 1385 | } 1386 | .fa-pied-piper-pp:before { 1387 | content: "\f1a7"; 1388 | } 1389 | .fa-pied-piper-alt:before { 1390 | content: "\f1a8"; 1391 | } 1392 | .fa-drupal:before { 1393 | content: "\f1a9"; 1394 | } 1395 | .fa-joomla:before { 1396 | content: "\f1aa"; 1397 | } 1398 | .fa-language:before { 1399 | content: "\f1ab"; 1400 | } 1401 | .fa-fax:before { 1402 | content: "\f1ac"; 1403 | } 1404 | .fa-building:before { 1405 | content: "\f1ad"; 1406 | } 1407 | .fa-child:before { 1408 | content: "\f1ae"; 1409 | } 1410 | .fa-paw:before { 1411 | content: "\f1b0"; 1412 | } 1413 | .fa-spoon:before { 1414 | content: "\f1b1"; 1415 | } 1416 | .fa-cube:before { 1417 | content: "\f1b2"; 1418 | } 1419 | .fa-cubes:before { 1420 | content: "\f1b3"; 1421 | } 1422 | .fa-behance:before { 1423 | content: "\f1b4"; 1424 | } 1425 | .fa-behance-square:before { 1426 | content: "\f1b5"; 1427 | } 1428 | .fa-steam:before { 1429 | content: "\f1b6"; 1430 | } 1431 | .fa-steam-square:before { 1432 | content: "\f1b7"; 1433 | } 1434 | .fa-recycle:before { 1435 | content: "\f1b8"; 1436 | } 1437 | .fa-automobile:before, 1438 | .fa-car:before { 1439 | content: "\f1b9"; 1440 | } 1441 | .fa-cab:before, 1442 | .fa-taxi:before { 1443 | content: "\f1ba"; 1444 | } 1445 | .fa-tree:before { 1446 | content: "\f1bb"; 1447 | } 1448 | .fa-spotify:before { 1449 | content: "\f1bc"; 1450 | } 1451 | .fa-deviantart:before { 1452 | content: "\f1bd"; 1453 | } 1454 | .fa-soundcloud:before { 1455 | content: "\f1be"; 1456 | } 1457 | .fa-database:before { 1458 | content: "\f1c0"; 1459 | } 1460 | .fa-file-pdf-o:before { 1461 | content: "\f1c1"; 1462 | } 1463 | .fa-file-word-o:before { 1464 | content: "\f1c2"; 1465 | } 1466 | .fa-file-excel-o:before { 1467 | content: "\f1c3"; 1468 | } 1469 | .fa-file-powerpoint-o:before { 1470 | content: "\f1c4"; 1471 | } 1472 | .fa-file-photo-o:before, 1473 | .fa-file-picture-o:before, 1474 | .fa-file-image-o:before { 1475 | content: "\f1c5"; 1476 | } 1477 | .fa-file-zip-o:before, 1478 | .fa-file-archive-o:before { 1479 | content: "\f1c6"; 1480 | } 1481 | .fa-file-sound-o:before, 1482 | .fa-file-audio-o:before { 1483 | content: "\f1c7"; 1484 | } 1485 | .fa-file-movie-o:before, 1486 | .fa-file-video-o:before { 1487 | content: "\f1c8"; 1488 | } 1489 | .fa-file-code-o:before { 1490 | content: "\f1c9"; 1491 | } 1492 | .fa-vine:before { 1493 | content: "\f1ca"; 1494 | } 1495 | .fa-codepen:before { 1496 | content: "\f1cb"; 1497 | } 1498 | .fa-jsfiddle:before { 1499 | content: "\f1cc"; 1500 | } 1501 | .fa-life-bouy:before, 1502 | .fa-life-buoy:before, 1503 | .fa-life-saver:before, 1504 | .fa-support:before, 1505 | .fa-life-ring:before { 1506 | content: "\f1cd"; 1507 | } 1508 | .fa-circle-o-notch:before { 1509 | content: "\f1ce"; 1510 | } 1511 | .fa-ra:before, 1512 | .fa-resistance:before, 1513 | .fa-rebel:before { 1514 | content: "\f1d0"; 1515 | } 1516 | .fa-ge:before, 1517 | .fa-empire:before { 1518 | content: "\f1d1"; 1519 | } 1520 | .fa-git-square:before { 1521 | content: "\f1d2"; 1522 | } 1523 | .fa-git:before { 1524 | content: "\f1d3"; 1525 | } 1526 | .fa-y-combinator-square:before, 1527 | .fa-yc-square:before, 1528 | .fa-hacker-news:before { 1529 | content: "\f1d4"; 1530 | } 1531 | .fa-tencent-weibo:before { 1532 | content: "\f1d5"; 1533 | } 1534 | .fa-qq:before { 1535 | content: "\f1d6"; 1536 | } 1537 | .fa-wechat:before, 1538 | .fa-weixin:before { 1539 | content: "\f1d7"; 1540 | } 1541 | .fa-send:before, 1542 | .fa-paper-plane:before { 1543 | content: "\f1d8"; 1544 | } 1545 | .fa-send-o:before, 1546 | .fa-paper-plane-o:before { 1547 | content: "\f1d9"; 1548 | } 1549 | .fa-history:before { 1550 | content: "\f1da"; 1551 | } 1552 | .fa-circle-thin:before { 1553 | content: "\f1db"; 1554 | } 1555 | .fa-header:before { 1556 | content: "\f1dc"; 1557 | } 1558 | .fa-paragraph:before { 1559 | content: "\f1dd"; 1560 | } 1561 | .fa-sliders:before { 1562 | content: "\f1de"; 1563 | } 1564 | .fa-share-alt:before { 1565 | content: "\f1e0"; 1566 | } 1567 | .fa-share-alt-square:before { 1568 | content: "\f1e1"; 1569 | } 1570 | .fa-bomb:before { 1571 | content: "\f1e2"; 1572 | } 1573 | .fa-soccer-ball-o:before, 1574 | .fa-futbol-o:before { 1575 | content: "\f1e3"; 1576 | } 1577 | .fa-tty:before { 1578 | content: "\f1e4"; 1579 | } 1580 | .fa-binoculars:before { 1581 | content: "\f1e5"; 1582 | } 1583 | .fa-plug:before { 1584 | content: "\f1e6"; 1585 | } 1586 | .fa-slideshare:before { 1587 | content: "\f1e7"; 1588 | } 1589 | .fa-twitch:before { 1590 | content: "\f1e8"; 1591 | } 1592 | .fa-yelp:before { 1593 | content: "\f1e9"; 1594 | } 1595 | .fa-newspaper-o:before { 1596 | content: "\f1ea"; 1597 | } 1598 | .fa-wifi:before { 1599 | content: "\f1eb"; 1600 | } 1601 | .fa-calculator:before { 1602 | content: "\f1ec"; 1603 | } 1604 | .fa-paypal:before { 1605 | content: "\f1ed"; 1606 | } 1607 | .fa-google-wallet:before { 1608 | content: "\f1ee"; 1609 | } 1610 | .fa-cc-visa:before { 1611 | content: "\f1f0"; 1612 | } 1613 | .fa-cc-mastercard:before { 1614 | content: "\f1f1"; 1615 | } 1616 | .fa-cc-discover:before { 1617 | content: "\f1f2"; 1618 | } 1619 | .fa-cc-amex:before { 1620 | content: "\f1f3"; 1621 | } 1622 | .fa-cc-paypal:before { 1623 | content: "\f1f4"; 1624 | } 1625 | .fa-cc-stripe:before { 1626 | content: "\f1f5"; 1627 | } 1628 | .fa-bell-slash:before { 1629 | content: "\f1f6"; 1630 | } 1631 | .fa-bell-slash-o:before { 1632 | content: "\f1f7"; 1633 | } 1634 | .fa-trash:before { 1635 | content: "\f1f8"; 1636 | } 1637 | .fa-copyright:before { 1638 | content: "\f1f9"; 1639 | } 1640 | .fa-at:before { 1641 | content: "\f1fa"; 1642 | } 1643 | .fa-eyedropper:before { 1644 | content: "\f1fb"; 1645 | } 1646 | .fa-paint-brush:before { 1647 | content: "\f1fc"; 1648 | } 1649 | .fa-birthday-cake:before { 1650 | content: "\f1fd"; 1651 | } 1652 | .fa-area-chart:before { 1653 | content: "\f1fe"; 1654 | } 1655 | .fa-pie-chart:before { 1656 | content: "\f200"; 1657 | } 1658 | .fa-line-chart:before { 1659 | content: "\f201"; 1660 | } 1661 | .fa-lastfm:before { 1662 | content: "\f202"; 1663 | } 1664 | .fa-lastfm-square:before { 1665 | content: "\f203"; 1666 | } 1667 | .fa-toggle-off:before { 1668 | content: "\f204"; 1669 | } 1670 | .fa-toggle-on:before { 1671 | content: "\f205"; 1672 | } 1673 | .fa-bicycle:before { 1674 | content: "\f206"; 1675 | } 1676 | .fa-bus:before { 1677 | content: "\f207"; 1678 | } 1679 | .fa-ioxhost:before { 1680 | content: "\f208"; 1681 | } 1682 | .fa-angellist:before { 1683 | content: "\f209"; 1684 | } 1685 | .fa-cc:before { 1686 | content: "\f20a"; 1687 | } 1688 | .fa-shekel:before, 1689 | .fa-sheqel:before, 1690 | .fa-ils:before { 1691 | content: "\f20b"; 1692 | } 1693 | .fa-meanpath:before { 1694 | content: "\f20c"; 1695 | } 1696 | .fa-buysellads:before { 1697 | content: "\f20d"; 1698 | } 1699 | .fa-connectdevelop:before { 1700 | content: "\f20e"; 1701 | } 1702 | .fa-dashcube:before { 1703 | content: "\f210"; 1704 | } 1705 | .fa-forumbee:before { 1706 | content: "\f211"; 1707 | } 1708 | .fa-leanpub:before { 1709 | content: "\f212"; 1710 | } 1711 | .fa-sellsy:before { 1712 | content: "\f213"; 1713 | } 1714 | .fa-shirtsinbulk:before { 1715 | content: "\f214"; 1716 | } 1717 | .fa-simplybuilt:before { 1718 | content: "\f215"; 1719 | } 1720 | .fa-skyatlas:before { 1721 | content: "\f216"; 1722 | } 1723 | .fa-cart-plus:before { 1724 | content: "\f217"; 1725 | } 1726 | .fa-cart-arrow-down:before { 1727 | content: "\f218"; 1728 | } 1729 | .fa-diamond:before { 1730 | content: "\f219"; 1731 | } 1732 | .fa-ship:before { 1733 | content: "\f21a"; 1734 | } 1735 | .fa-user-secret:before { 1736 | content: "\f21b"; 1737 | } 1738 | .fa-motorcycle:before { 1739 | content: "\f21c"; 1740 | } 1741 | .fa-street-view:before { 1742 | content: "\f21d"; 1743 | } 1744 | .fa-heartbeat:before { 1745 | content: "\f21e"; 1746 | } 1747 | .fa-venus:before { 1748 | content: "\f221"; 1749 | } 1750 | .fa-mars:before { 1751 | content: "\f222"; 1752 | } 1753 | .fa-mercury:before { 1754 | content: "\f223"; 1755 | } 1756 | .fa-intersex:before, 1757 | .fa-transgender:before { 1758 | content: "\f224"; 1759 | } 1760 | .fa-transgender-alt:before { 1761 | content: "\f225"; 1762 | } 1763 | .fa-venus-double:before { 1764 | content: "\f226"; 1765 | } 1766 | .fa-mars-double:before { 1767 | content: "\f227"; 1768 | } 1769 | .fa-venus-mars:before { 1770 | content: "\f228"; 1771 | } 1772 | .fa-mars-stroke:before { 1773 | content: "\f229"; 1774 | } 1775 | .fa-mars-stroke-v:before { 1776 | content: "\f22a"; 1777 | } 1778 | .fa-mars-stroke-h:before { 1779 | content: "\f22b"; 1780 | } 1781 | .fa-neuter:before { 1782 | content: "\f22c"; 1783 | } 1784 | .fa-genderless:before { 1785 | content: "\f22d"; 1786 | } 1787 | .fa-facebook-official:before { 1788 | content: "\f230"; 1789 | } 1790 | .fa-pinterest-p:before { 1791 | content: "\f231"; 1792 | } 1793 | .fa-whatsapp:before { 1794 | content: "\f232"; 1795 | } 1796 | .fa-server:before { 1797 | content: "\f233"; 1798 | } 1799 | .fa-user-plus:before { 1800 | content: "\f234"; 1801 | } 1802 | .fa-user-times:before { 1803 | content: "\f235"; 1804 | } 1805 | .fa-hotel:before, 1806 | .fa-bed:before { 1807 | content: "\f236"; 1808 | } 1809 | .fa-viacoin:before { 1810 | content: "\f237"; 1811 | } 1812 | .fa-train:before { 1813 | content: "\f238"; 1814 | } 1815 | .fa-subway:before { 1816 | content: "\f239"; 1817 | } 1818 | .fa-medium:before { 1819 | content: "\f23a"; 1820 | } 1821 | .fa-yc:before, 1822 | .fa-y-combinator:before { 1823 | content: "\f23b"; 1824 | } 1825 | .fa-optin-monster:before { 1826 | content: "\f23c"; 1827 | } 1828 | .fa-opencart:before { 1829 | content: "\f23d"; 1830 | } 1831 | .fa-expeditedssl:before { 1832 | content: "\f23e"; 1833 | } 1834 | .fa-battery-4:before, 1835 | .fa-battery:before, 1836 | .fa-battery-full:before { 1837 | content: "\f240"; 1838 | } 1839 | .fa-battery-3:before, 1840 | .fa-battery-three-quarters:before { 1841 | content: "\f241"; 1842 | } 1843 | .fa-battery-2:before, 1844 | .fa-battery-half:before { 1845 | content: "\f242"; 1846 | } 1847 | .fa-battery-1:before, 1848 | .fa-battery-quarter:before { 1849 | content: "\f243"; 1850 | } 1851 | .fa-battery-0:before, 1852 | .fa-battery-empty:before { 1853 | content: "\f244"; 1854 | } 1855 | .fa-mouse-pointer:before { 1856 | content: "\f245"; 1857 | } 1858 | .fa-i-cursor:before { 1859 | content: "\f246"; 1860 | } 1861 | .fa-object-group:before { 1862 | content: "\f247"; 1863 | } 1864 | .fa-object-ungroup:before { 1865 | content: "\f248"; 1866 | } 1867 | .fa-sticky-note:before { 1868 | content: "\f249"; 1869 | } 1870 | .fa-sticky-note-o:before { 1871 | content: "\f24a"; 1872 | } 1873 | .fa-cc-jcb:before { 1874 | content: "\f24b"; 1875 | } 1876 | .fa-cc-diners-club:before { 1877 | content: "\f24c"; 1878 | } 1879 | .fa-clone:before { 1880 | content: "\f24d"; 1881 | } 1882 | .fa-balance-scale:before { 1883 | content: "\f24e"; 1884 | } 1885 | .fa-hourglass-o:before { 1886 | content: "\f250"; 1887 | } 1888 | .fa-hourglass-1:before, 1889 | .fa-hourglass-start:before { 1890 | content: "\f251"; 1891 | } 1892 | .fa-hourglass-2:before, 1893 | .fa-hourglass-half:before { 1894 | content: "\f252"; 1895 | } 1896 | .fa-hourglass-3:before, 1897 | .fa-hourglass-end:before { 1898 | content: "\f253"; 1899 | } 1900 | .fa-hourglass:before { 1901 | content: "\f254"; 1902 | } 1903 | .fa-hand-grab-o:before, 1904 | .fa-hand-rock-o:before { 1905 | content: "\f255"; 1906 | } 1907 | .fa-hand-stop-o:before, 1908 | .fa-hand-paper-o:before { 1909 | content: "\f256"; 1910 | } 1911 | .fa-hand-scissors-o:before { 1912 | content: "\f257"; 1913 | } 1914 | .fa-hand-lizard-o:before { 1915 | content: "\f258"; 1916 | } 1917 | .fa-hand-spock-o:before { 1918 | content: "\f259"; 1919 | } 1920 | .fa-hand-pointer-o:before { 1921 | content: "\f25a"; 1922 | } 1923 | .fa-hand-peace-o:before { 1924 | content: "\f25b"; 1925 | } 1926 | .fa-trademark:before { 1927 | content: "\f25c"; 1928 | } 1929 | .fa-registered:before { 1930 | content: "\f25d"; 1931 | } 1932 | .fa-creative-commons:before { 1933 | content: "\f25e"; 1934 | } 1935 | .fa-gg:before { 1936 | content: "\f260"; 1937 | } 1938 | .fa-gg-circle:before { 1939 | content: "\f261"; 1940 | } 1941 | .fa-tripadvisor:before { 1942 | content: "\f262"; 1943 | } 1944 | .fa-odnoklassniki:before { 1945 | content: "\f263"; 1946 | } 1947 | .fa-odnoklassniki-square:before { 1948 | content: "\f264"; 1949 | } 1950 | .fa-get-pocket:before { 1951 | content: "\f265"; 1952 | } 1953 | .fa-wikipedia-w:before { 1954 | content: "\f266"; 1955 | } 1956 | .fa-safari:before { 1957 | content: "\f267"; 1958 | } 1959 | .fa-chrome:before { 1960 | content: "\f268"; 1961 | } 1962 | .fa-firefox:before { 1963 | content: "\f269"; 1964 | } 1965 | .fa-opera:before { 1966 | content: "\f26a"; 1967 | } 1968 | .fa-internet-explorer:before { 1969 | content: "\f26b"; 1970 | } 1971 | .fa-tv:before, 1972 | .fa-television:before { 1973 | content: "\f26c"; 1974 | } 1975 | .fa-contao:before { 1976 | content: "\f26d"; 1977 | } 1978 | .fa-500px:before { 1979 | content: "\f26e"; 1980 | } 1981 | .fa-amazon:before { 1982 | content: "\f270"; 1983 | } 1984 | .fa-calendar-plus-o:before { 1985 | content: "\f271"; 1986 | } 1987 | .fa-calendar-minus-o:before { 1988 | content: "\f272"; 1989 | } 1990 | .fa-calendar-times-o:before { 1991 | content: "\f273"; 1992 | } 1993 | .fa-calendar-check-o:before { 1994 | content: "\f274"; 1995 | } 1996 | .fa-industry:before { 1997 | content: "\f275"; 1998 | } 1999 | .fa-map-pin:before { 2000 | content: "\f276"; 2001 | } 2002 | .fa-map-signs:before { 2003 | content: "\f277"; 2004 | } 2005 | .fa-map-o:before { 2006 | content: "\f278"; 2007 | } 2008 | .fa-map:before { 2009 | content: "\f279"; 2010 | } 2011 | .fa-commenting:before { 2012 | content: "\f27a"; 2013 | } 2014 | .fa-commenting-o:before { 2015 | content: "\f27b"; 2016 | } 2017 | .fa-houzz:before { 2018 | content: "\f27c"; 2019 | } 2020 | .fa-vimeo:before { 2021 | content: "\f27d"; 2022 | } 2023 | .fa-black-tie:before { 2024 | content: "\f27e"; 2025 | } 2026 | .fa-fonticons:before { 2027 | content: "\f280"; 2028 | } 2029 | .fa-reddit-alien:before { 2030 | content: "\f281"; 2031 | } 2032 | .fa-edge:before { 2033 | content: "\f282"; 2034 | } 2035 | .fa-credit-card-alt:before { 2036 | content: "\f283"; 2037 | } 2038 | .fa-codiepie:before { 2039 | content: "\f284"; 2040 | } 2041 | .fa-modx:before { 2042 | content: "\f285"; 2043 | } 2044 | .fa-fort-awesome:before { 2045 | content: "\f286"; 2046 | } 2047 | .fa-usb:before { 2048 | content: "\f287"; 2049 | } 2050 | .fa-product-hunt:before { 2051 | content: "\f288"; 2052 | } 2053 | .fa-mixcloud:before { 2054 | content: "\f289"; 2055 | } 2056 | .fa-scribd:before { 2057 | content: "\f28a"; 2058 | } 2059 | .fa-pause-circle:before { 2060 | content: "\f28b"; 2061 | } 2062 | .fa-pause-circle-o:before { 2063 | content: "\f28c"; 2064 | } 2065 | .fa-stop-circle:before { 2066 | content: "\f28d"; 2067 | } 2068 | .fa-stop-circle-o:before { 2069 | content: "\f28e"; 2070 | } 2071 | .fa-shopping-bag:before { 2072 | content: "\f290"; 2073 | } 2074 | .fa-shopping-basket:before { 2075 | content: "\f291"; 2076 | } 2077 | .fa-hashtag:before { 2078 | content: "\f292"; 2079 | } 2080 | .fa-bluetooth:before { 2081 | content: "\f293"; 2082 | } 2083 | .fa-bluetooth-b:before { 2084 | content: "\f294"; 2085 | } 2086 | .fa-percent:before { 2087 | content: "\f295"; 2088 | } 2089 | .fa-gitlab:before { 2090 | content: "\f296"; 2091 | } 2092 | .fa-wpbeginner:before { 2093 | content: "\f297"; 2094 | } 2095 | .fa-wpforms:before { 2096 | content: "\f298"; 2097 | } 2098 | .fa-envira:before { 2099 | content: "\f299"; 2100 | } 2101 | .fa-universal-access:before { 2102 | content: "\f29a"; 2103 | } 2104 | .fa-wheelchair-alt:before { 2105 | content: "\f29b"; 2106 | } 2107 | .fa-question-circle-o:before { 2108 | content: "\f29c"; 2109 | } 2110 | .fa-blind:before { 2111 | content: "\f29d"; 2112 | } 2113 | .fa-audio-description:before { 2114 | content: "\f29e"; 2115 | } 2116 | .fa-volume-control-phone:before { 2117 | content: "\f2a0"; 2118 | } 2119 | .fa-braille:before { 2120 | content: "\f2a1"; 2121 | } 2122 | .fa-assistive-listening-systems:before { 2123 | content: "\f2a2"; 2124 | } 2125 | .fa-asl-interpreting:before, 2126 | .fa-american-sign-language-interpreting:before { 2127 | content: "\f2a3"; 2128 | } 2129 | .fa-deafness:before, 2130 | .fa-hard-of-hearing:before, 2131 | .fa-deaf:before { 2132 | content: "\f2a4"; 2133 | } 2134 | .fa-glide:before { 2135 | content: "\f2a5"; 2136 | } 2137 | .fa-glide-g:before { 2138 | content: "\f2a6"; 2139 | } 2140 | .fa-signing:before, 2141 | .fa-sign-language:before { 2142 | content: "\f2a7"; 2143 | } 2144 | .fa-low-vision:before { 2145 | content: "\f2a8"; 2146 | } 2147 | .fa-viadeo:before { 2148 | content: "\f2a9"; 2149 | } 2150 | .fa-viadeo-square:before { 2151 | content: "\f2aa"; 2152 | } 2153 | .fa-snapchat:before { 2154 | content: "\f2ab"; 2155 | } 2156 | .fa-snapchat-ghost:before { 2157 | content: "\f2ac"; 2158 | } 2159 | .fa-snapchat-square:before { 2160 | content: "\f2ad"; 2161 | } 2162 | .fa-pied-piper:before { 2163 | content: "\f2ae"; 2164 | } 2165 | .fa-first-order:before { 2166 | content: "\f2b0"; 2167 | } 2168 | .fa-yoast:before { 2169 | content: "\f2b1"; 2170 | } 2171 | .fa-themeisle:before { 2172 | content: "\f2b2"; 2173 | } 2174 | .fa-google-plus-circle:before, 2175 | .fa-google-plus-official:before { 2176 | content: "\f2b3"; 2177 | } 2178 | .fa-fa:before, 2179 | .fa-font-awesome:before { 2180 | content: "\f2b4"; 2181 | } 2182 | .fa-handshake-o:before { 2183 | content: "\f2b5"; 2184 | } 2185 | .fa-envelope-open:before { 2186 | content: "\f2b6"; 2187 | } 2188 | .fa-envelope-open-o:before { 2189 | content: "\f2b7"; 2190 | } 2191 | .fa-linode:before { 2192 | content: "\f2b8"; 2193 | } 2194 | .fa-address-book:before { 2195 | content: "\f2b9"; 2196 | } 2197 | .fa-address-book-o:before { 2198 | content: "\f2ba"; 2199 | } 2200 | .fa-vcard:before, 2201 | .fa-address-card:before { 2202 | content: "\f2bb"; 2203 | } 2204 | .fa-vcard-o:before, 2205 | .fa-address-card-o:before { 2206 | content: "\f2bc"; 2207 | } 2208 | .fa-user-circle:before { 2209 | content: "\f2bd"; 2210 | } 2211 | .fa-user-circle-o:before { 2212 | content: "\f2be"; 2213 | } 2214 | .fa-user-o:before { 2215 | content: "\f2c0"; 2216 | } 2217 | .fa-id-badge:before { 2218 | content: "\f2c1"; 2219 | } 2220 | .fa-drivers-license:before, 2221 | .fa-id-card:before { 2222 | content: "\f2c2"; 2223 | } 2224 | .fa-drivers-license-o:before, 2225 | .fa-id-card-o:before { 2226 | content: "\f2c3"; 2227 | } 2228 | .fa-quora:before { 2229 | content: "\f2c4"; 2230 | } 2231 | .fa-free-code-camp:before { 2232 | content: "\f2c5"; 2233 | } 2234 | .fa-telegram:before { 2235 | content: "\f2c6"; 2236 | } 2237 | .fa-thermometer-4:before, 2238 | .fa-thermometer:before, 2239 | .fa-thermometer-full:before { 2240 | content: "\f2c7"; 2241 | } 2242 | .fa-thermometer-3:before, 2243 | .fa-thermometer-three-quarters:before { 2244 | content: "\f2c8"; 2245 | } 2246 | .fa-thermometer-2:before, 2247 | .fa-thermometer-half:before { 2248 | content: "\f2c9"; 2249 | } 2250 | .fa-thermometer-1:before, 2251 | .fa-thermometer-quarter:before { 2252 | content: "\f2ca"; 2253 | } 2254 | .fa-thermometer-0:before, 2255 | .fa-thermometer-empty:before { 2256 | content: "\f2cb"; 2257 | } 2258 | .fa-shower:before { 2259 | content: "\f2cc"; 2260 | } 2261 | .fa-bathtub:before, 2262 | .fa-s15:before, 2263 | .fa-bath:before { 2264 | content: "\f2cd"; 2265 | } 2266 | .fa-podcast:before { 2267 | content: "\f2ce"; 2268 | } 2269 | .fa-window-maximize:before { 2270 | content: "\f2d0"; 2271 | } 2272 | .fa-window-minimize:before { 2273 | content: "\f2d1"; 2274 | } 2275 | .fa-window-restore:before { 2276 | content: "\f2d2"; 2277 | } 2278 | .fa-times-rectangle:before, 2279 | .fa-window-close:before { 2280 | content: "\f2d3"; 2281 | } 2282 | .fa-times-rectangle-o:before, 2283 | .fa-window-close-o:before { 2284 | content: "\f2d4"; 2285 | } 2286 | .fa-bandcamp:before { 2287 | content: "\f2d5"; 2288 | } 2289 | .fa-grav:before { 2290 | content: "\f2d6"; 2291 | } 2292 | .fa-etsy:before { 2293 | content: "\f2d7"; 2294 | } 2295 | .fa-imdb:before { 2296 | content: "\f2d8"; 2297 | } 2298 | .fa-ravelry:before { 2299 | content: "\f2d9"; 2300 | } 2301 | .fa-eercast:before { 2302 | content: "\f2da"; 2303 | } 2304 | .fa-microchip:before { 2305 | content: "\f2db"; 2306 | } 2307 | .fa-snowflake-o:before { 2308 | content: "\f2dc"; 2309 | } 2310 | .fa-superpowers:before { 2311 | content: "\f2dd"; 2312 | } 2313 | .fa-wpexplorer:before { 2314 | content: "\f2de"; 2315 | } 2316 | .fa-meetup:before { 2317 | content: "\f2e0"; 2318 | } 2319 | .sr-only { 2320 | position: absolute; 2321 | width: 1px; 2322 | height: 1px; 2323 | padding: 0; 2324 | margin: -1px; 2325 | overflow: hidden; 2326 | clip: rect(0, 0, 0, 0); 2327 | border: 0; 2328 | } 2329 | .sr-only-focusable:active, 2330 | .sr-only-focusable:focus { 2331 | position: static; 2332 | width: auto; 2333 | height: auto; 2334 | margin: 0; 2335 | overflow: visible; 2336 | clip: auto; 2337 | } 2338 | -------------------------------------------------------------------------------- /webservice/ui/src/index.css: -------------------------------------------------------------------------------- 1 | @import './constants/constants.css'; 2 | @import 'font-awesome.css'; 3 | 4 | html { 5 | font-size: 62.5%; 6 | } 7 | 8 | body { 9 | margin: 0; 10 | background: var(--my-white); 11 | color: var(--my-white); 12 | font-family: 'Intel Clear', sans-serif; 13 | font-weight: 300; 14 | font-style: normal; 15 | overflow-x: hidden; 16 | width: 100vw; 17 | position: relative; 18 | /*text-transform: uppercase;*/ 19 | } 20 | 21 | @font-face { 22 | font-family: 'Intel Clear'; 23 | src: url('./assets/fonts/IntelClear-Light.eot'); 24 | src: url('./assets/fonts/IntelClear-Light.eot?#iefix') format('embedded-opentype'), url('./assets/fonts/IntelClear-Light.woff') format('woff'), url('./assets/fonts/IntelClear-Light.ttf') format('truetype'); 25 | font-weight: 300; 26 | font-style: normal; 27 | } 28 | 29 | @font-face { 30 | font-family: 'Intel Clear'; 31 | src: url('./assets/fonts/IntelClear-LightItalic.eot'); 32 | src: url('./assets/fonts/IntelClear-LightItalic.eot?#iefix') format('embedded-opentype'), url('./assets/fonts/IntelClear-LightItalic.woff') format('woff'), url('./assets/fonts/IntelClear-LightItalic.ttf') format('truetype'); 33 | font-weight: 300; 34 | font-style: italic; 35 | } 36 | 37 | @font-face { 38 | font-family: 'Intel Clear'; 39 | src: url('./assets/fonts/IntelClear-Regular.eot'); 40 | src: url('./assets/fonts/IntelClear-Regular.eot?#iefix') format('embedded-opentype'), url('./assets/fonts/IntelClear-Regular.woff') format('woff'), url('./assets/fonts/IntelClear-Regular.ttf') format('truetype'); 41 | font-weight: normal; 42 | font-style: normal; 43 | } 44 | 45 | @font-face { 46 | font-family: 'Intel Clear'; 47 | src: url('./assets/fonts/IntelClear-Italic.eot'); 48 | src: url('./assets/fonts/IntelClear-Italic.eot?#iefix') format('embedded-opentype'), url('./assets/fonts/IntelClear-Italic.woff') format('woff'), url('./assets/fonts/IntelClear-Italic.ttf') format('truetype'); 49 | font-weight: normal; 50 | font-style: italic; 51 | } 52 | 53 | @font-face { 54 | font-family: 'Intel Clear'; 55 | src: url('./assets/fonts/IntelClear-Bold.eot'); 56 | src: url('./assets/fonts/IntelClear-Bold.eot?#iefix') format('embedded-opentype'), url('./assets/fonts/IntelClear-Bold.woff') format('woff'), url('./assets/fonts/IntelClear-Bold.ttf') format('truetype'); 57 | font-weight: bold; 58 | font-style: normal; 59 | } 60 | 61 | @font-face { 62 | font-family: 'Intel Clear'; 63 | src: url('./assets/fonts/IntelClear-BoldItalic.eot'); 64 | src: url('./assets/fonts/IntelClear-BoldItalic.eot?#iefix') format('embedded-opentype'), url('./assets/fonts/IntelClear-BoldItalic.woff') format('woff'), url('./assets/fonts/IntelClear-BoldItalic.ttf') format('truetype'); 65 | font-weight: bold; 66 | font-style: italic; 67 | } 68 | -------------------------------------------------------------------------------- /webservice/ui/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Pedestrian Counter 8 | 9 | 10 |
11 | 12 | -------------------------------------------------------------------------------- /webservice/ui/src/index.jsx: -------------------------------------------------------------------------------- 1 | // all paths start from src/ unless using relative paths 2 | // libs 3 | import React from "react"; 4 | import { render } from "react-dom"; 5 | import { Provider } from "react-redux"; 6 | import { createStore, applyMiddleware } from "redux"; 7 | import { composeWithDevTools } from 'redux-devtools-extension'; 8 | import ReduxThunk from "redux-thunk"; 9 | import createHistory from "history/createBrowserHistory"; 10 | import { Route, Switch } from "react-router-dom"; 11 | import { ConnectedRouter, routerMiddleware } from "react-router-redux"; 12 | 13 | // components 14 | import ConnectedNavigation from "components/navigation/ConnectedNavigation"; 15 | 16 | // features 17 | import ConnectedLog from "features/stats/ConnectedStats"; 18 | 19 | // reducers 20 | import reducers from "dux/reducers"; 21 | 22 | // pages 23 | import Monitor from "pages/monitor/Monitor"; 24 | 25 | // css 26 | import "index.css"; 27 | 28 | // html 29 | import "index.html"; 30 | 31 | // Create a history of your choosing (we"re using a browser history in this case) 32 | const history = createHistory(); 33 | 34 | // Build the middleware for intercepting and dispatching navigation actions 35 | const middleware = [ 36 | ReduxThunk, 37 | routerMiddleware( history ), 38 | ]; 39 | 40 | // the store 41 | // const store = createStore( reducers, applyMiddleware( ...middleware ) ); 42 | const store = createStore( reducers, composeWithDevTools( 43 | applyMiddleware( ...middleware ), 44 | // other store enhancers if any 45 | ) ); 46 | 47 | let StatsOn = false; 48 | 49 | const togglePanel = function(){ 50 | console.log( "toggling panel"); 51 | StatsOn = !StatsOn; 52 | } 53 | 54 | 55 | // the app 56 | render( 57 | 58 | 59 |
60 | 61 | 62 | 63 |
64 |
65 |
, 66 | document.getElementById( "app" ), 67 | ); 68 | -------------------------------------------------------------------------------- /webservice/ui/src/pages/monitor/Monitor.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import CameraFeed from "../../features/camera-feed/CameraFeed"; 4 | 5 | class Monitor extends React.Component { 6 | constructor( props ) { 7 | super( props ); 8 | } 9 | 10 | render() { 11 | return ( 12 | 13 | ); 14 | } 15 | } 16 | 17 | Monitor.propTypes = { 18 | // this provides route info, will cause lint error 19 | match: PropTypes.object, 20 | }; 21 | 22 | Monitor.defaultProps = { 23 | match: {}, 24 | }; 25 | 26 | export default Monitor; -------------------------------------------------------------------------------- /webservice/ui/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require( "webpack" ); 2 | var html = require( "html-webpack-plugin" ); 3 | var merge = require( "webpack-merge" ); 4 | var path = require( "path" ); 5 | var cssimport = require( "postcss-import" ); 6 | var cssnext = require( "postcss-cssnext" ); 7 | var cssmixins = require( "postcss-mixins" ); 8 | var extractor = require( "extract-text-webpack-plugin" ); 9 | var devConfig = require( "./webpack.dev.config" ); 10 | var distConfig = require( "./webpack.dist.config" ); 11 | var CopyWebpackPlugin = require( "copy-webpack-plugin" ); 12 | 13 | var SRC_DIR = path.resolve( __dirname, "src" ); 14 | 15 | var devCss = { 16 | test: /\.css$/, 17 | use: [ 18 | { 19 | loader: "style-loader", 20 | }, 21 | { 22 | loader: "css-loader", 23 | }, 24 | { 25 | loader: "postcss-loader", 26 | options: { 27 | plugins: function () { 28 | return [ 29 | cssimport, 30 | cssnext, 31 | cssmixins, 32 | ]; 33 | }, 34 | }, 35 | }, 36 | ], 37 | }; 38 | 39 | var distCss = { 40 | test: /\.css$/, 41 | use: extractor.extract( { 42 | fallback: "style-loader", 43 | use: [ 44 | { 45 | loader: "css-loader", 46 | }, 47 | { 48 | loader: "postcss-loader", 49 | options: { 50 | plugins: function () { 51 | return [ 52 | cssimport, 53 | cssnext, 54 | cssmixins, 55 | ]; 56 | }, 57 | }, 58 | }, 59 | ], 60 | } ), 61 | }; 62 | 63 | var cssRule; 64 | switch ( process.env.NODE_ENV ) { 65 | case "development": 66 | cssRule = devCss; 67 | break; 68 | case "production": 69 | cssRule = distCss; 70 | break; 71 | } 72 | 73 | var config = { 74 | entry: SRC_DIR + "/index.jsx", 75 | output: { 76 | publicPath: "/", 77 | }, 78 | module: { 79 | rules: [ 80 | { 81 | test: /\.html$/, 82 | use: "html-loader", 83 | }, 84 | cssRule, 85 | { 86 | test: /\.js$|\.jsx$/, 87 | exclude: /(node_modules|dist)/, 88 | use: [ { 89 | loader: "babel-loader", 90 | options: { 91 | presets: [ "es2015", "react", "stage-1" ], 92 | }, 93 | } ], 94 | }, 95 | { 96 | test: /.(ttf|otf|eot|svg|jpg|woff(2)?)(\?[a-z0-9]+)?$/, 97 | use: { 98 | loader: "url-loader", 99 | }, 100 | }, 101 | ], 102 | }, 103 | resolve: { 104 | extensions: [ ".js", ".jsx" ], 105 | modules: [ 106 | path.resolve( SRC_DIR ), 107 | path.resolve( "./node_modules" ), 108 | ], 109 | }, 110 | plugins: [ 111 | new webpack.HotModuleReplacementPlugin(), 112 | new html( { 113 | template: SRC_DIR + "/index.html", 114 | } ), 115 | new CopyWebpackPlugin( [ 116 | { from: SRC_DIR + "/assets/", to: "assets/" }, 117 | ] ), 118 | ], 119 | }; 120 | 121 | // npm run dist vs npm run dev needs different config settings 122 | switch ( process.env.NODE_ENV ) { 123 | case "development": 124 | config = merge( config, devConfig ); 125 | break; 126 | case "production": 127 | config = merge( config, distConfig ); 128 | break; 129 | } 130 | 131 | module.exports = config; 132 | -------------------------------------------------------------------------------- /webservice/ui/webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | devtool: "inline-source-map", 5 | devServer: { 6 | hot: true, 7 | port: 3000, 8 | host: "0.0.0.0", 9 | historyApiFallback: true, 10 | disableHostCheck: true, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /webservice/ui/webpack.dist.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var path = require( "path" ); 4 | var webpack = require( "webpack" ); 5 | var clean = require( "clean-webpack-plugin" ); 6 | var cssimport = require( "postcss-import" ); 7 | var cssnext = require( "postcss-cssnext" ); 8 | var extractor = require( "extract-text-webpack-plugin" ); 9 | 10 | // dist config 11 | module.exports = { 12 | output: { 13 | path: path.resolve( __dirname, "dist" ), 14 | publicPath: "./", 15 | filename: "app.js", 16 | }, 17 | plugins: [ 18 | new extractor( "styles.css" ), 19 | new webpack.DefinePlugin( { 20 | "process.env": { 21 | "NODE_ENV": JSON.stringify( "production" ), 22 | }, 23 | } ), 24 | new clean( [ 25 | path.resolve( __dirname, "dist" ), 26 | ] ), 27 | new webpack.optimize.UglifyJsPlugin( { 28 | compressor: { warnings: true }, 29 | } ), 30 | ], 31 | }; 32 | -------------------------------------------------------------------------------- /windows-setup.md: -------------------------------------------------------------------------------- 1 | # Windows - Initial Setup 2 | 3 | ### Install the Windows Subsystem for Linux (WSL) 4 | 5 | To get started, install the [Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10). Select the Ubuntu 16 distribution. 6 | 7 | Make sure to follow the instructions on the [Initialize a Distribution](https://docs.microsoft.com/en-us/windows/wsl/initialize-distro) page as well, especially the following command with the terminal window: 8 | 9 | ``` 10 | sudo apt update && sudo apt upgrade 11 | ``` 12 | 13 | This will allow you to essentially work within the Linux environment for your project. To make it easier to work with various commands, we suggest within the WSL terminal to right-click the menu bar, select “Properties”, and "Use Ctrl+Shift+C/V As Copy/Paste", so you can paste into the terminal (make note of the “Shift” included in this command vs. just `Ctrl+C` or `Ctrl+V`). 14 | 15 | Next, you’ll follow the Linux Setup instructions, but do note there are a couple of additional notes before moving on from the OpenVINO™ Toolkit installation in the Linux Setup instructions: 16 | 17 | 1. Use your normal browser in Windows to navigate to the OpenVINO™ installation website. Once you get a link to download OpenVINO™ **for Linux** (right-click on the full package download and copy the link over), use `wget` within the WSL terminal to download it. 18 | 19 | ``` 20 | wget {openvino download URL} 21 | ``` 22 | 23 | Note that this will download the `.tgz` file in your current directory. You can then further follow the Linux instructions to extract and install these files. 24 | 25 | 2. Note that when you run the verification scripts at the end of installing OpenVINO™, the WSL terminal will not allow the second script to open a display window. As long as the first script runs fine and the second script only fails at displaying the window, everything should be installed fine. 26 | 27 | ### Install NPM 28 | 29 | Follow the instructions in the main README file under “Install npm” to make sure the relevant NPM libraries are installed for the included Node servers. 30 | 31 | ### Use Visual Studios with WSL 32 | 33 | So far, you have been stuck working from the WSL terminal, and likely don’t want to edit all your code from within there. You can follow the instructions [here](https://code.visualstudio.com/docs/remote/wsl) to link the Visual Studios IDE to WSL, and allow you to edit your code files outside of the terminal window. 34 | 35 | ### Other WSL Questions 36 | 37 | If you have additional questions specific to using WSL, see the FAQ [here](https://docs.microsoft.com/en-us/windows/wsl/faq). 38 | --------------------------------------------------------------------------------