Compare commits
93 Commits
7f1a85d430
...
master
Author | SHA1 | Date | |
---|---|---|---|
324d2ac7f4 | |||
8a0212a139 | |||
802806b4c2 | |||
1d4bf2d0b6 | |||
79dcb4d75a | |||
7c67fa7de0 | |||
89a416ac38 | |||
e9ffe514dd | |||
6a579772df | |||
20a4b4d349 | |||
0085b95198 | |||
579481ce16 | |||
645f2bb44b | |||
c3bbbd3d13 | |||
0df5b350d9 | |||
265a59d4c3 | |||
e9fcfd4ffa | |||
3b5b544a3e | |||
57998e3bc5 | |||
9cae969803 | |||
37ea775d59 | |||
c9c631b947 | |||
433b235929 | |||
7588b58453 | |||
251a00eaa6 | |||
5768b78619 | |||
08df5a624b | |||
c0ae0a30fe | |||
8970d4fec3 | |||
dbad96be95 | |||
8764e1b45a | |||
6e61fc7756 | |||
f0af8f08e3 | |||
76c3787107 | |||
544df78ac8 | |||
02df37692f | |||
ed4e207ff0 | |||
a5a37fdd1b | |||
1b52e16db5 | |||
c9550ea5b8 | |||
3de33014e5 | |||
a458ad1712 | |||
030b259d32 | |||
022852b163 | |||
2d5eca233e | |||
9d6e0cd453 | |||
a2c3112f81 | |||
c7e3fcabcf | |||
70c4c91035 | |||
aa39597541 | |||
bcbab1383a | |||
bab0984c30 | |||
0667c6ec39 | |||
0d71899ce1 | |||
966d8bd79e | |||
5b42ce9f43 | |||
85320d3cf3 | |||
9d55c39e33 | |||
d75e9d378d | |||
3cdaa2dc35 | |||
9af0ba1bb8 | |||
04c27560ea | |||
73157520ab | |||
f862e4b8da | |||
f85346aea9 | |||
1d438bc349 | |||
86999cd646 | |||
8979ad6db3 | |||
779e21909c | |||
f84ea8f0c5 | |||
181985859e | |||
39a38fe8eb | |||
490f0f3265 | |||
41bf14a4e5 | |||
3f267cb009 | |||
0e3eb8720f | |||
3f2467f6c6 | |||
2a83856622 | |||
d726273431 | |||
412647617b | |||
53ff60ae5a | |||
f273823c93 | |||
c92808ac7d | |||
f13133829a | |||
4c1258dc33 | |||
a52f6acd71 | |||
4281c2d524 | |||
639e6edac6 | |||
3632e56dc4 | |||
b87d90e5a4 | |||
f1c1ad69cc | |||
f102b89a21 | |||
e1a596dae9 |
25
.dockerignore
Normal file
25
.dockerignore
Normal file
@ -0,0 +1,25 @@
|
||||
**/.classpath
|
||||
**/.dockerignore
|
||||
**/.env
|
||||
**/.git
|
||||
**/.gitignore
|
||||
**/.project
|
||||
**/.settings
|
||||
**/.toolstarget
|
||||
**/.vs
|
||||
**/.vscode
|
||||
**/*.*proj.user
|
||||
**/*.dbmdl
|
||||
**/*.jfm
|
||||
**/azds.yaml
|
||||
**/docs
|
||||
**/bin
|
||||
**/docker-compose*
|
||||
**/Dockerfile*
|
||||
**/node_modules
|
||||
**/npm-debug.log
|
||||
**/obj
|
||||
**/secrets.dev.yaml
|
||||
**/values.dev.yaml
|
||||
LICENSE
|
||||
README.md
|
45
.drone.yml
Normal file
45
.drone.yml
Normal file
@ -0,0 +1,45 @@
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: default
|
||||
|
||||
steps:
|
||||
- name: code-analysis
|
||||
image: aosapps/drone-sonar-plugin
|
||||
settings:
|
||||
sonar_host:
|
||||
from_secret: SONAR_HOST
|
||||
sonar_token:
|
||||
from_secret: SONAR_CODE
|
||||
|
||||
- name: kaniko
|
||||
image: banzaicloud/drone-kaniko
|
||||
settings:
|
||||
registry: registry.kmlabz.com
|
||||
repo: birbnetes/${DRONE_REPO_NAME}
|
||||
username:
|
||||
from_secret: DOCKER_USERNAME
|
||||
password:
|
||||
from_secret: DOCKER_PASSWORD
|
||||
tags:
|
||||
- latest
|
||||
- ${DRONE_BUILD_NUMBER}
|
||||
|
||||
- name: dockerhub
|
||||
image: plugins/docker
|
||||
settings:
|
||||
repo: birbnetes/${DRONE_REPO_NAME}
|
||||
username:
|
||||
from_secret: DOCKERHUB_USER
|
||||
password:
|
||||
from_secret: DOCKERHUB_PASSWORD
|
||||
tags:
|
||||
- latest
|
||||
- ${DRONE_BUILD_NUMBER}
|
||||
|
||||
- name: ms-teams
|
||||
image: kuperiu/drone-teams
|
||||
settings:
|
||||
webhook:
|
||||
from_secret: TEAMS_WEBHOOK
|
||||
when:
|
||||
status: [ failure ]
|
@ -1,12 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
||||
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<SpaRoot>ClientApp\</SpaRoot>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
|
||||
<AssemblyName>Birdmap.API</AssemblyName>
|
||||
<UserSecretsId>a919c854-b332-49ee-8e38-96549f828836</UserSecretsId>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<DockerComposeProjectPath>..\docker-compose.dcproj</DockerComposeProjectPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
@ -20,9 +24,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="10.1.1" />
|
||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="3.1.9" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.9">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@ -30,7 +34,12 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.10.9" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="NLog" Version="4.7.5" />
|
||||
<PackageReference Include="NLog.Web" Version="4.9.3" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" Version="4.9.3" />
|
||||
<PackageReference Include="NSwag.AspNetCore" Version="13.9.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -42,21 +51,19 @@
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="ClientApp\src\common\components\BirdmapTitle.tsx" />
|
||||
<None Remove="ClientApp\src\common\ErrorDispatcher.ts" />
|
||||
<None Remove="ClientApp\src\common\ServiceBase.ts" />
|
||||
<None Remove="ClientApp\src\components\auth\Auth.tsx" />
|
||||
<None Remove="ClientApp\src\components\auth\AuthClient.ts" />
|
||||
<None Remove="ClientApp\src\components\auth\AuthService.ts" />
|
||||
<None Remove="ClientApp\src\components\dashboard\ServiceInfoService.ts" />
|
||||
<None Remove="ClientApp\src\components\devices\DeviceService.ts" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<TypeScriptCompile Include="ClientApp\src\components\auth\Auth.tsx" />
|
||||
<TypeScriptCompile Include="ClientApp\src\common\components\BirdmapTitle.tsx" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="ClientApp\src\components\dashboard\" />
|
||||
<Folder Include="ClientApp\src\components\devices\" />
|
||||
<Folder Include="ClientApp\src\components\heatmap\" />
|
||||
<Folder Include="ClientApp\src\common\components\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
354
Birdmap.API/ClientApp/package-lock.json
generated
354
Birdmap.API/ClientApp/package-lock.json
generated
@ -1199,6 +1199,11 @@
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
|
||||
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
|
||||
},
|
||||
"@googlemaps/js-api-loader": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.8.0.tgz",
|
||||
"integrity": "sha512-aFxlJVFOC00KELhlaqU6tpnxj9szVdG7OSHxQzc9uhp4Ky8/zvYNnzZg2S+pPvhe+WkJPugQL8fowP5nYTBXUg=="
|
||||
},
|
||||
"@hapi/address": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
|
||||
@ -1424,6 +1429,11 @@
|
||||
"@types/yargs": "^13.0.0"
|
||||
}
|
||||
},
|
||||
"@mapbox/point-geometry": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz",
|
||||
"integrity": "sha1-ioP5M1x4YO/6Lu7KJUMyqgru2PI="
|
||||
},
|
||||
"@material-ui/core": {
|
||||
"version": "4.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.0.tgz",
|
||||
@ -1559,6 +1569,33 @@
|
||||
"react-is": "^16.8.0"
|
||||
}
|
||||
},
|
||||
"@microsoft/signalr": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-5.0.0.tgz",
|
||||
"integrity": "sha512-AsbU1ZB4Q9JsZ77W13VGT8gi/cVrFn3XbvVfULSwrC9DVCXF2JpkBDh0cCmRaYs9M3kqKohiVM1WPqNeAGil/g==",
|
||||
"requires": {
|
||||
"abort-controller": "^3.0.0",
|
||||
"eventsource": "^1.0.7",
|
||||
"fetch-cookie": "^0.7.3",
|
||||
"node-fetch": "^2.6.0",
|
||||
"ws": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"node-fetch": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
|
||||
},
|
||||
"ws": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
|
||||
"integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
|
||||
"requires": {
|
||||
"async-limiter": "~1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@mrmlnc/readdir-enhanced": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
|
||||
@ -1573,6 +1610,25 @@
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz",
|
||||
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
|
||||
},
|
||||
"@rollup/plugin-babel": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.2.1.tgz",
|
||||
"integrity": "sha512-Jd7oqFR2dzZJ3NWANDyBjwTtX/lYbZpVcmkHrfQcpvawHs9E4c0nYk5U2mfZ6I/DZcIvy506KZJi54XK/jxH7A==",
|
||||
"requires": {
|
||||
"@babel/helper-module-imports": "^7.10.4",
|
||||
"@rollup/pluginutils": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"@rollup/pluginutils": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
|
||||
"integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
|
||||
"requires": {
|
||||
"@types/estree": "0.0.39",
|
||||
"estree-walker": "^1.0.1",
|
||||
"picomatch": "^2.2.2"
|
||||
}
|
||||
},
|
||||
"@svgr/babel-plugin-add-jsx-attribute": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz",
|
||||
@ -1724,6 +1780,11 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
|
||||
"integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag=="
|
||||
},
|
||||
"@types/estree": {
|
||||
"version": "0.0.39",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
|
||||
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw=="
|
||||
},
|
||||
"@types/glob": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz",
|
||||
@ -2170,6 +2231,14 @@
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
|
||||
"integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q=="
|
||||
},
|
||||
"abort-controller": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||
"requires": {
|
||||
"event-target-shim": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"accepts": {
|
||||
"version": "1.3.7",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
||||
@ -2311,6 +2380,20 @@
|
||||
"normalize-path": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"apexcharts": {
|
||||
"version": "3.22.2",
|
||||
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.22.2.tgz",
|
||||
"integrity": "sha512-pR+cmApk7dhfYILBpe8RVb+FdLfVCt/RDWvAJO1F5feeSQ8lKDgFkRuVu9KOeEarHVXjUpnhLqHNMx7YaprK8A==",
|
||||
"requires": {
|
||||
"@rollup/plugin-babel": "^5.2.1",
|
||||
"svg.draggable.js": "^2.2.2",
|
||||
"svg.easing.js": "^2.0.0",
|
||||
"svg.filter.js": "^2.0.2",
|
||||
"svg.pathmorphing.js": "^0.1.3",
|
||||
"svg.resize.js": "^1.4.3",
|
||||
"svg.select.js": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
||||
@ -3406,6 +3489,11 @@
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
|
||||
},
|
||||
"can-use-dom": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/can-use-dom/-/can-use-dom-0.1.0.tgz",
|
||||
"integrity": "sha1-IsxKNKCrxDlQ9CxkEQJKP2NmtFo="
|
||||
},
|
||||
"caniuse-api": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
|
||||
@ -3450,6 +3538,11 @@
|
||||
"supports-color": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"change-emitter": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz",
|
||||
"integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU="
|
||||
},
|
||||
"chardet": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
|
||||
@ -4857,6 +4950,24 @@
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
|
||||
},
|
||||
"encoding": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
|
||||
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
|
||||
"requires": {
|
||||
"iconv-lite": "^0.6.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"iconv-lite": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
|
||||
"integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==",
|
||||
"requires": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"end-of-stream": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
@ -4972,6 +5083,11 @@
|
||||
"next-tick": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"es6-denodeify": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/es6-denodeify/-/es6-denodeify-0.1.5.tgz",
|
||||
"integrity": "sha1-MdTV/pxVA+ElRgQ5MQ4WoqPznB8="
|
||||
},
|
||||
"es6-iterator": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
|
||||
@ -5488,6 +5604,11 @@
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
|
||||
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="
|
||||
},
|
||||
"estree-walker": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
|
||||
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg=="
|
||||
},
|
||||
"esutils": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
|
||||
@ -5498,6 +5619,11 @@
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
|
||||
},
|
||||
"event-target-shim": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
|
||||
},
|
||||
"eventemitter3": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||
@ -5857,6 +5983,44 @@
|
||||
"bser": "2.1.1"
|
||||
}
|
||||
},
|
||||
"fbjs": {
|
||||
"version": "0.8.17",
|
||||
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
|
||||
"integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
|
||||
"requires": {
|
||||
"core-js": "^1.0.0",
|
||||
"isomorphic-fetch": "^2.1.1",
|
||||
"loose-envify": "^1.0.0",
|
||||
"object-assign": "^4.1.0",
|
||||
"promise": "^7.1.1",
|
||||
"setimmediate": "^1.0.5",
|
||||
"ua-parser-js": "^0.7.18"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
|
||||
"integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
|
||||
},
|
||||
"promise": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
|
||||
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
|
||||
"requires": {
|
||||
"asap": "~2.0.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"fetch-cookie": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.7.3.tgz",
|
||||
"integrity": "sha512-rZPkLnI8x5V+zYAiz8QonAHsTb4BY+iFowFBI1RFn0zrO343AVp9X7/yUj/9wL6Ef/8fLls8b/vGtzUvmyAUGA==",
|
||||
"requires": {
|
||||
"es6-denodeify": "^0.1.1",
|
||||
"tough-cookie": "^2.3.3"
|
||||
}
|
||||
},
|
||||
"figgy-pudding": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
|
||||
@ -6326,6 +6490,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"google-map-react": {
|
||||
"version": "2.1.9",
|
||||
"resolved": "https://registry.npmjs.org/google-map-react/-/google-map-react-2.1.9.tgz",
|
||||
"integrity": "sha512-//Pa0o6sdspU2H0ehVztSDQSnYYeV6TY4Z6ftty34yiCJYLliOzeq17dA9uFkyUFdL+XwbTU6e9mfs+bjBMIzw==",
|
||||
"requires": {
|
||||
"@googlemaps/js-api-loader": "^1.7.0",
|
||||
"@mapbox/point-geometry": "^0.1.0",
|
||||
"eventemitter3": "^4.0.4",
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"google-maps": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/google-maps/-/google-maps-4.3.3.tgz",
|
||||
@ -6334,6 +6509,11 @@
|
||||
"@types/googlemaps": "^3.39.1"
|
||||
}
|
||||
},
|
||||
"google-maps-infobox": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/google-maps-infobox/-/google-maps-infobox-2.0.0.tgz",
|
||||
"integrity": "sha512-hTuWmWZZSOxf5D/z7l3/hTF1grgRvLG53BEKMdjiKOG+FcK/kH7vqseUeyIU9Zj2ZIqKTOaro0nknxpAuRq4Vw=="
|
||||
},
|
||||
"google-maps-react": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/google-maps-react/-/google-maps-react-2.0.6.tgz",
|
||||
@ -7207,6 +7387,15 @@
|
||||
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
|
||||
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
|
||||
},
|
||||
"isomorphic-fetch": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
|
||||
"integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
|
||||
"requires": {
|
||||
"node-fetch": "^1.0.1",
|
||||
"whatwg-fetch": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
@ -8321,6 +8510,16 @@
|
||||
"object-visit": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"marker-clusterer-plus": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/marker-clusterer-plus/-/marker-clusterer-plus-2.1.4.tgz",
|
||||
"integrity": "sha1-+O/3TVmdqzt9Dj/tUmTqDnBPXWc="
|
||||
},
|
||||
"markerwithlabel": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/markerwithlabel/-/markerwithlabel-2.0.2.tgz",
|
||||
"integrity": "sha512-C/cbm1A0h/u54gwHk5ZJNdUU3V3+1BbCpRPMsMyFA7vF4yL+aB4rWpxACz29TpQ+cTg6/iQroExh0PMSRGtQFg=="
|
||||
},
|
||||
"md5.js": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||
@ -8734,6 +8933,15 @@
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
|
||||
"integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
|
||||
"requires": {
|
||||
"encoding": "^0.1.11",
|
||||
"is-stream": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node-forge": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
|
||||
@ -10634,6 +10842,14 @@
|
||||
"prop-types": "^15.6.2"
|
||||
}
|
||||
},
|
||||
"react-apexcharts": {
|
||||
"version": "1.3.7",
|
||||
"resolved": "https://registry.npmjs.org/react-apexcharts/-/react-apexcharts-1.3.7.tgz",
|
||||
"integrity": "sha512-2OFhEHd70/WHN0kmrJtVx37UfaL71ZogVkwezmDqwQWgwhK6upuhlnEEX7tEq4xvjA+RFDn6hiUTNIuC/Q7Zqw==",
|
||||
"requires": {
|
||||
"prop-types": "^15.5.7"
|
||||
}
|
||||
},
|
||||
"react-app-polyfill": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-1.0.6.tgz",
|
||||
@ -10809,6 +11025,50 @@
|
||||
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz",
|
||||
"integrity": "sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA=="
|
||||
},
|
||||
"react-google-maps": {
|
||||
"version": "9.4.5",
|
||||
"resolved": "https://registry.npmjs.org/react-google-maps/-/react-google-maps-9.4.5.tgz",
|
||||
"integrity": "sha512-8z5nX9DxIcBCXuEiurmRT1VXVwnzx0C6+3Es6lxB2/OyY2SLax2/LcDu6Aldxnl3HegefTL7NJzGeaKAJ61pOA==",
|
||||
"requires": {
|
||||
"babel-runtime": "^6.11.6",
|
||||
"can-use-dom": "^0.1.0",
|
||||
"google-maps-infobox": "^2.0.0",
|
||||
"invariant": "^2.2.1",
|
||||
"lodash": "^4.16.2",
|
||||
"marker-clusterer-plus": "^2.1.4",
|
||||
"markerwithlabel": "^2.0.1",
|
||||
"prop-types": "^15.5.8",
|
||||
"recompose": "^0.26.0",
|
||||
"scriptjs": "^2.5.8",
|
||||
"warning": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"hoist-non-react-statics": {
|
||||
"version": "2.5.5",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
|
||||
"integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
|
||||
},
|
||||
"recompose": {
|
||||
"version": "0.26.0",
|
||||
"resolved": "https://registry.npmjs.org/recompose/-/recompose-0.26.0.tgz",
|
||||
"integrity": "sha512-KwOu6ztO0mN5vy3+zDcc45lgnaUoaQse/a5yLVqtzTK13czSWnFGmXbQVmnoMgDkI5POd1EwIKSbjU1V7xdZog==",
|
||||
"requires": {
|
||||
"change-emitter": "^0.1.2",
|
||||
"fbjs": "^0.8.1",
|
||||
"hoist-non-react-statics": "^2.3.1",
|
||||
"symbol-observable": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"warning": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
|
||||
"integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
|
||||
"requires": {
|
||||
"loose-envify": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.12.0",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz",
|
||||
@ -11189,6 +11449,26 @@
|
||||
"util.promisify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"recompose": {
|
||||
"version": "0.30.0",
|
||||
"resolved": "https://registry.npmjs.org/recompose/-/recompose-0.30.0.tgz",
|
||||
"integrity": "sha512-ZTrzzUDa9AqUIhRk4KmVFihH0rapdCSMFXjhHbNrjAWxBuUD/guYlyysMnuHjlZC/KRiOKRtB4jf96yYSkKE8w==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.0.0",
|
||||
"change-emitter": "^0.1.2",
|
||||
"fbjs": "^0.8.1",
|
||||
"hoist-non-react-statics": "^2.3.1",
|
||||
"react-lifecycles-compat": "^3.0.2",
|
||||
"symbol-observable": "^1.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"hoist-non-react-statics": {
|
||||
"version": "2.5.5",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
|
||||
"integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"recursive-readdir": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",
|
||||
@ -11748,6 +12028,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"scriptjs": {
|
||||
"version": "2.5.9",
|
||||
"resolved": "https://registry.npmjs.org/scriptjs/-/scriptjs-2.5.9.tgz",
|
||||
"integrity": "sha512-qGVDoreyYiP1pkQnbnFAUIS5AjenNwwQBdl7zeos9etl+hYKWahjRTfzAZZYBv5xNHx7vNKCmaLDQZ6Fr2AEXg=="
|
||||
},
|
||||
"seamless-immutable": {
|
||||
"version": "7.1.4",
|
||||
"resolved": "https://registry.npmjs.org/seamless-immutable/-/seamless-immutable-7.1.4.tgz",
|
||||
@ -12832,6 +13117,70 @@
|
||||
"resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz",
|
||||
"integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ=="
|
||||
},
|
||||
"svg.draggable.js": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz",
|
||||
"integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==",
|
||||
"requires": {
|
||||
"svg.js": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"svg.easing.js": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz",
|
||||
"integrity": "sha1-iqmUawqOJ4V6XEChDrpAkeVpHxI=",
|
||||
"requires": {
|
||||
"svg.js": ">=2.3.x"
|
||||
}
|
||||
},
|
||||
"svg.filter.js": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz",
|
||||
"integrity": "sha1-kQCOFROJ3ZIwd5/L5uLJo2LRwgM=",
|
||||
"requires": {
|
||||
"svg.js": "^2.2.5"
|
||||
}
|
||||
},
|
||||
"svg.js": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz",
|
||||
"integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA=="
|
||||
},
|
||||
"svg.pathmorphing.js": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz",
|
||||
"integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==",
|
||||
"requires": {
|
||||
"svg.js": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"svg.resize.js": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz",
|
||||
"integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==",
|
||||
"requires": {
|
||||
"svg.js": "^2.6.5",
|
||||
"svg.select.js": "^2.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"svg.select.js": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz",
|
||||
"integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==",
|
||||
"requires": {
|
||||
"svg.js": "^2.2.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"svg.select.js": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz",
|
||||
"integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==",
|
||||
"requires": {
|
||||
"svg.js": "^2.6.5"
|
||||
}
|
||||
},
|
||||
"svgo": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.0.tgz",
|
||||
@ -13262,6 +13611,11 @@
|
||||
"integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==",
|
||||
"dev": true
|
||||
},
|
||||
"ua-parser-js": {
|
||||
"version": "0.7.22",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.22.tgz",
|
||||
"integrity": "sha512-YUxzMjJ5T71w6a8WWVcMGM6YWOTX27rCoIQgLXiWaxqXSx9D7DNjiGWn1aJIRSQ5qr0xuhra77bSIh6voR/46Q=="
|
||||
},
|
||||
"unicode-canonical-property-names-ecmascript": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
|
||||
|
@ -6,8 +6,11 @@
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "^4.0.0-alpha.56",
|
||||
"@microsoft/signalr": "^5.0.0",
|
||||
"apexcharts": "^3.22.2",
|
||||
"bootstrap": "^4.3.1",
|
||||
"connected-react-router": "6.5.2",
|
||||
"google-map-react": "^2.1.9",
|
||||
"google-maps": "^4.3.3",
|
||||
"google-maps-react": "^2.0.6",
|
||||
"history": "4.10.1",
|
||||
@ -15,12 +18,15 @@
|
||||
"merge": "1.2.1",
|
||||
"popper.js": "^1.16.0",
|
||||
"react": "^16.11.0",
|
||||
"react-apexcharts": "^1.3.7",
|
||||
"react-dom": "16.11.0",
|
||||
"react-google-maps": "^9.4.5",
|
||||
"react-redux": "7.1.1",
|
||||
"react-router": "5.1.2",
|
||||
"react-router-dom": "5.1.2",
|
||||
"react-scripts": "^3.4.4",
|
||||
"reactstrap": "8.1.1",
|
||||
"recompose": "^0.30.0",
|
||||
"redux": "4.0.4",
|
||||
"redux-thunk": "2.3.0",
|
||||
"svgo": "1.3.0"
|
||||
|
@ -23,10 +23,20 @@
|
||||
<title>Birdmap</title>
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
body {
|
||||
height: 100vh;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
body::-webkit-scrollbar {
|
||||
display:none;
|
||||
}
|
||||
</style>
|
||||
<noscript>
|
||||
You need to enable JavaScript to run this app.
|
||||
</noscript>
|
||||
<div id="root"></div>
|
||||
<div id="root" style="height: 100vh;"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
@ -1,37 +1,38 @@
|
||||
import { Box, Container, IconButton, Menu, MenuItem, MenuList, Paper, Grow, Popper } from '@material-ui/core';
|
||||
import AccountCircle from '@material-ui/icons/AccountCircle';
|
||||
import AppBar from '@material-ui/core/AppBar';
|
||||
import blue from '@material-ui/core/colors/blue';
|
||||
import orange from '@material-ui/core/colors/orange';
|
||||
import { Box, Paper } from '@material-ui/core';
|
||||
import { blueGrey, grey, orange } from '@material-ui/core/colors';
|
||||
import { createMuiTheme, createStyles, makeStyles, Theme } from '@material-ui/core/styles';
|
||||
import Toolbar from '@material-ui/core/Toolbar';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { ThemeProvider } from '@material-ui/styles';
|
||||
import React, { useState, } from 'react';
|
||||
import { BrowserRouter, NavLink, Redirect, Route, Switch, Link } from 'react-router-dom';
|
||||
import BirdmapTitle from './common/components/BirdmapTitle';
|
||||
import React, { useState } from 'react';
|
||||
import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom';
|
||||
import BirdmapBar from './components/appBar/BirdmapBar';
|
||||
import Auth from './components/auth/Auth';
|
||||
import AuthService from './components/auth/AuthService';
|
||||
import { ClickAwayListener } from '@material-ui/core';
|
||||
|
||||
import Dashboard from './components/dashboard/Dashboard';
|
||||
import Devices from './components/devices/Devices';
|
||||
import MapContainer from './components/heatmap/Heatmap';
|
||||
import Logs from './components/logs/Logs';
|
||||
import DevicesContextProvider from './contexts/DevicesContextProvider';
|
||||
|
||||
const theme = createMuiTheme({
|
||||
palette: {
|
||||
primary: {
|
||||
main: blue[900],
|
||||
main: blueGrey[900],
|
||||
dark: grey[400],
|
||||
},
|
||||
secondary: {
|
||||
main: orange[200],
|
||||
main: blueGrey[700],
|
||||
dark: blueGrey[50],
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
function App() {
|
||||
|
||||
const [authenticated, setAuthenticated] = useState(AuthService.isAuthenticated());
|
||||
const [isAdmin, setIsAdmin] = useState(AuthService.isAdmin());
|
||||
|
||||
const onAuthenticated = () => {
|
||||
setAuthenticated(AuthService.isAuthenticated());
|
||||
setIsAdmin(AuthService.isAdmin());
|
||||
};
|
||||
|
||||
const AuthComponent = () => {
|
||||
@ -40,26 +41,68 @@ function App() {
|
||||
);
|
||||
}
|
||||
|
||||
const LogsComponent = () => {
|
||||
return <Logs/>
|
||||
}
|
||||
|
||||
const DashboardComponent = () => {
|
||||
return <Typography>Dashboard</Typography>;
|
||||
return <Dashboard isAdmin={isAdmin}/>;
|
||||
};
|
||||
|
||||
const DevicesComponent = () => {
|
||||
return <Typography>Devices</Typography>;
|
||||
return <Devices isAdmin={isAdmin}/>;
|
||||
|
||||
};
|
||||
|
||||
const HeatmapComponent = () => {
|
||||
return <Typography>Heatmap</Typography>;
|
||||
return (
|
||||
<Paper elevation={0}>
|
||||
<MapContainer />
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
||||
const HeaderComponent = () => {
|
||||
return (
|
||||
<BirdmapBar onLogout={AuthService.logout} isAdmin={isAdmin} isAuthenticated={authenticated}/>
|
||||
);
|
||||
}
|
||||
|
||||
const PredicateRoute = ({ component: Component, predicate: Predicate, ...rest }: { [x: string]: any, component: any, predicate: any }) => {
|
||||
return (
|
||||
<PredicateRouteInternal {...rest} header={HeaderComponent} body={Component} predicate={Predicate}/>
|
||||
);
|
||||
}
|
||||
|
||||
const PublicRoute = ({ component: Component, ...rest }: { [x: string]: any, component: any }) => {
|
||||
return (
|
||||
<PredicateRoute {...rest} component={Component} predicate={true}/>
|
||||
);
|
||||
}
|
||||
|
||||
const PrivateRoute = ({ component: Component, ...rest }: { [x: string]: any, component: any }) => {
|
||||
return (
|
||||
<PredicateRoute {...rest} component={Component} predicate={authenticated}/>
|
||||
);
|
||||
}
|
||||
|
||||
const AdminRoute = ({ component: Component, ...rest }: { [x: string]: any, component: any }) => {
|
||||
return (
|
||||
<PredicateRoute {...rest} component={Component} predicate={authenticated && isAdmin}/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<BrowserRouter>
|
||||
<Switch>
|
||||
<PublicRoute path="/login" component={AuthComponent} />
|
||||
<PrivateRoute path="/" exact authenticated={authenticated} component={DashboardComponent} />
|
||||
<PrivateRoute path="/devices" exact authenticated={authenticated} component={DevicesComponent} />
|
||||
<PrivateRoute path="/heatmap" exact authenticated={authenticated} component={HeatmapComponent} />
|
||||
<PublicRoute exact path="/login" component={AuthComponent} />
|
||||
<AdminRoute exact path="/logs" component={LogsComponent} />
|
||||
<DevicesContextProvider>
|
||||
<PrivateRoute exact path="/" component={DashboardComponent} />
|
||||
<PrivateRoute exact path="/devices/:id?" component={DevicesComponent} />
|
||||
<PrivateRoute exact path="/heatmap" component={HeatmapComponent} />
|
||||
</DevicesContextProvider>
|
||||
</Switch>
|
||||
</BrowserRouter>
|
||||
</ThemeProvider>
|
||||
@ -68,112 +111,26 @@ function App() {
|
||||
|
||||
export default App;
|
||||
|
||||
const PublicRoute = ({ component: Component, ...rest }: { [x: string]: any, component: any}) => {
|
||||
const PredicateRouteInternal = ({ header: HeaderComponent, body: BodyComponent, predicate: Predicate, ...rest }: { [x: string]: any, header: any, body: any, predicate: any }) => {
|
||||
return (
|
||||
<Route {...rest} render={matchProps => (
|
||||
<DefaultLayout component={Component} authenticated={false} {...matchProps} />
|
||||
)} />
|
||||
);
|
||||
}
|
||||
|
||||
const PrivateRoute = ({ component: Component, authenticated: Authenticated, ...rest }: { [x: string]: any, component: any, authenticated: any }) => {
|
||||
return (
|
||||
<Route {...rest} render={matchProps => (
|
||||
Authenticated
|
||||
? <DefaultLayout component={Component} authenticated={Authenticated} {...matchProps} />
|
||||
Predicate
|
||||
? <DefaultLayoutInternal header={HeaderComponent} body={BodyComponent} {...matchProps} />
|
||||
: <Redirect to='/login' />
|
||||
)} />
|
||||
);
|
||||
};
|
||||
|
||||
const DefaultLayout = ({ component: Component, authenticated: Authenticated, ...rest }: { [x: string]: any, component: any, authenticated: any }) => {
|
||||
const DefaultLayoutInternal = ({ header: HeaderComponent, body: BodyComponent, ...rest }: { [x: string]: any, header: any, body: any }) => {
|
||||
const classes = useDefaultLayoutStyles();
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const anchorRef = React.useRef<HTMLButtonElement>(null);
|
||||
|
||||
const handleToggle = () => {
|
||||
setOpen((prevOpen) => !prevOpen);
|
||||
};
|
||||
|
||||
const handleClose = (event: React.MouseEvent<EventTarget>) => {
|
||||
if (anchorRef.current && anchorRef.current.contains(event.target as HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const handleLogout = (event: React.MouseEvent<EventTarget>) => {
|
||||
if (anchorRef.current && anchorRef.current.contains(event.target as HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
AuthService.logout();
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
function handleListKeyDown(event: React.KeyboardEvent) {
|
||||
if (event.key === 'Tab') {
|
||||
event.preventDefault();
|
||||
setOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
const prevOpen = React.useRef(open);
|
||||
React.useEffect(() => {
|
||||
if (prevOpen.current === true && open === false) {
|
||||
anchorRef.current!.focus();
|
||||
}
|
||||
|
||||
prevOpen.current = open;
|
||||
}, [open]);
|
||||
|
||||
|
||||
const renderNavLinks = () => {
|
||||
return Authenticated
|
||||
? <Container className={classes.nav_menu}>
|
||||
<NavLink exact to="/" className={classes.nav_menu_item} activeClassName={classes.nav_menu_item_active}>Dashboard</NavLink>
|
||||
<NavLink exact to="/devices" className={classes.nav_menu_item} activeClassName={classes.nav_menu_item_active}>Devices</NavLink>
|
||||
<NavLink exact to="/heatmap" className={classes.nav_menu_item} activeClassName={classes.nav_menu_item_active}>Heatmap</NavLink>
|
||||
<IconButton className={classes.nav_menu_icon}
|
||||
ref={anchorRef}
|
||||
aria-haspopup="true"
|
||||
aria-controls={open ? 'menu-list-grow' : undefined}
|
||||
aria-label="account of current user"
|
||||
onClick={handleToggle}>
|
||||
<AccountCircle/>
|
||||
</IconButton>
|
||||
<Popper open={open} anchorEl={anchorRef.current} role={undefined} transition disablePortal>
|
||||
{({ TransitionProps, placement }) => (
|
||||
<Grow
|
||||
{...TransitionProps}
|
||||
style={{ transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom' }}>
|
||||
<Paper>
|
||||
<ClickAwayListener onClickAway={handleClose}>
|
||||
<MenuList autoFocusItem={open} id="menu-list-grow" onKeyDown={handleListKeyDown}>
|
||||
<MenuItem onClick={handleLogout} component={Link} {...{ to: '/login' }}>Logout</MenuItem>
|
||||
</MenuList>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Grow>
|
||||
)}
|
||||
</Popper>
|
||||
</Container>
|
||||
: null;
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<AppBar position="static">
|
||||
<Toolbar>
|
||||
<BirdmapTitle />
|
||||
<Typography component={'span'} className={classes.typo}>
|
||||
{renderNavLinks()}
|
||||
</Typography>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<Box style={{ margin: '32px' }}>
|
||||
<Component {...rest} />
|
||||
<Box className={classes.header}>
|
||||
<HeaderComponent />
|
||||
</Box>
|
||||
<Box className={classes.body}>
|
||||
<BodyComponent {...rest} />
|
||||
</Box>
|
||||
</React.Fragment>
|
||||
);
|
||||
@ -181,39 +138,12 @@ const DefaultLayout = ({ component: Component, authenticated: Authenticated, ...
|
||||
|
||||
const useDefaultLayoutStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
typo: {
|
||||
marginLeft: 'auto',
|
||||
color: 'white',
|
||||
header: {
|
||||
height: '7%',
|
||||
},
|
||||
nav_menu: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
nav_menu_icon: {
|
||||
color: 'inherit',
|
||||
marginLeft: '24px',
|
||||
'&:hover': {
|
||||
color: 'inherit',
|
||||
body: {
|
||||
backgroundColor: theme.palette.primary.dark,
|
||||
height: '93%',
|
||||
}
|
||||
},
|
||||
nav_menu_item: {
|
||||
textDecoration: 'none',
|
||||
fontWeight: 'normal',
|
||||
color: 'inherit',
|
||||
marginLeft: '24px',
|
||||
'&:hover': {
|
||||
color: 'inherit',
|
||||
}
|
||||
},
|
||||
nav_menu_item_active: {
|
||||
textDecoration: 'underline',
|
||||
fontWeight: 'bold',
|
||||
color: 'inherit',
|
||||
marginLeft: '24px',
|
||||
'&:hover': {
|
||||
color: 'inherit',
|
||||
}
|
||||
},
|
||||
}),
|
||||
);
|
5
Birdmap.API/ClientApp/src/common/Constants.js
Normal file
5
Birdmap.API/ClientApp/src/common/Constants.js
Normal file
@ -0,0 +1,5 @@
|
||||
export default {
|
||||
probability_method_name: 'NotifyMessagesAsync',
|
||||
update_method_name: 'NotifyDeviceUpdatedAsync',
|
||||
update_all_method_name: 'NotifyAllUpdatedAsync',
|
||||
};
|
@ -21,7 +21,7 @@ var __extends = (this && this.__extends) || (function () {
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ApiException = exports.SensorStatus = exports.Coordinates = exports.DeviceStatus = exports.Sensor = exports.Device = exports.DeviceService = void 0;
|
||||
exports.ApiException = exports.SensorStatus = exports.Coordinates = exports.DeviceStatus = exports.Sensor = exports.Device = void 0;
|
||||
var DeviceService = /** @class */ (function () {
|
||||
function DeviceService(baseUrl, http) {
|
||||
this.jsonParseReviver = undefined;
|
||||
@ -38,7 +38,8 @@ var DeviceService = /** @class */ (function () {
|
||||
var options_ = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
"Accept": "application/json",
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
return this.http.fetch(url_, options_).then(function (_response) {
|
||||
@ -89,7 +90,9 @@ var DeviceService = /** @class */ (function () {
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
var options_ = {
|
||||
method: "POST",
|
||||
headers: {}
|
||||
headers: {
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
return this.http.fetch(url_, options_).then(function (_response) {
|
||||
return _this.processOfflineall(_response);
|
||||
@ -129,7 +132,9 @@ var DeviceService = /** @class */ (function () {
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
var options_ = {
|
||||
method: "POST",
|
||||
headers: {}
|
||||
headers: {
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
return this.http.fetch(url_, options_).then(function (_response) {
|
||||
return _this.processOnlineall(_response);
|
||||
@ -174,7 +179,8 @@ var DeviceService = /** @class */ (function () {
|
||||
var options_ = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
"Accept": "application/json",
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
return this.http.fetch(url_, options_).then(function (_response) {
|
||||
@ -223,7 +229,9 @@ var DeviceService = /** @class */ (function () {
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
var options_ = {
|
||||
method: "POST",
|
||||
headers: {}
|
||||
headers: {
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
return this.http.fetch(url_, options_).then(function (_response) {
|
||||
return _this.processOfflinedevice(_response);
|
||||
@ -267,7 +275,9 @@ var DeviceService = /** @class */ (function () {
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
var options_ = {
|
||||
method: "POST",
|
||||
headers: {}
|
||||
headers: {
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
return this.http.fetch(url_, options_).then(function (_response) {
|
||||
return _this.processOnlinedevice(_response);
|
||||
@ -316,7 +326,8 @@ var DeviceService = /** @class */ (function () {
|
||||
var options_ = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
"Accept": "application/json",
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
return this.http.fetch(url_, options_).then(function (_response) {
|
||||
@ -369,7 +380,9 @@ var DeviceService = /** @class */ (function () {
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
var options_ = {
|
||||
method: "POST",
|
||||
headers: {}
|
||||
headers: {
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
return this.http.fetch(url_, options_).then(function (_response) {
|
||||
return _this.processOfflinesensor(_response);
|
||||
@ -417,7 +430,9 @@ var DeviceService = /** @class */ (function () {
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
var options_ = {
|
||||
method: "POST",
|
||||
headers: {}
|
||||
headers: {
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
return this.http.fetch(url_, options_).then(function (_response) {
|
||||
return _this.processOnlinesensor(_response);
|
||||
@ -449,7 +464,7 @@ var DeviceService = /** @class */ (function () {
|
||||
};
|
||||
return DeviceService;
|
||||
}());
|
||||
exports.DeviceService = DeviceService;
|
||||
exports.default = DeviceService;
|
||||
var Device = /** @class */ (function () {
|
||||
function Device(data) {
|
||||
if (data) {
|
1
Birdmap.API/ClientApp/src/common/DeviceService.js.map
Normal file
1
Birdmap.API/ClientApp/src/common/DeviceService.js.map
Normal file
File diff suppressed because one or more lines are too long
@ -7,7 +7,7 @@
|
||||
//----------------------
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
export class DeviceService {
|
||||
export default class DeviceService {
|
||||
private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
|
||||
private baseUrl: string;
|
||||
protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
|
||||
@ -27,7 +27,8 @@ export class DeviceService {
|
||||
let options_ = <RequestInit>{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
"Accept": "application/json",
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
|
||||
@ -73,6 +74,7 @@ export class DeviceService {
|
||||
let options_ = <RequestInit>{
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
|
||||
@ -111,6 +113,7 @@ export class DeviceService {
|
||||
let options_ = <RequestInit>{
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
|
||||
@ -153,7 +156,8 @@ export class DeviceService {
|
||||
let options_ = <RequestInit>{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
"Accept": "application/json",
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
|
||||
@ -199,6 +203,7 @@ export class DeviceService {
|
||||
let options_ = <RequestInit>{
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
|
||||
@ -241,6 +246,7 @@ export class DeviceService {
|
||||
let options_ = <RequestInit>{
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
|
||||
@ -287,7 +293,8 @@ export class DeviceService {
|
||||
let options_ = <RequestInit>{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
"Accept": "application/json",
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
|
||||
@ -337,6 +344,7 @@ export class DeviceService {
|
||||
let options_ = <RequestInit>{
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
|
||||
@ -383,6 +391,7 @@ export class DeviceService {
|
||||
let options_ = <RequestInit>{
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
|
@ -1,14 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var ErrorDispatcher = {
|
||||
errorHandlers: [],
|
||||
registerErrorHandler: function (errorHandlerFn) {
|
||||
this.errorHandlers.push(errorHandlerFn);
|
||||
},
|
||||
raiseError: function (errorMessage) {
|
||||
for (var i = 0; i < this.errorHandlers.length; i++)
|
||||
this.errorHandlers[i](errorMessage);
|
||||
}
|
||||
};
|
||||
exports.default = ErrorDispatcher;
|
||||
//# sourceMappingURL=ErrorDispatcher.js.map
|
@ -1 +0,0 @@
|
||||
{"version":3,"file":"ErrorDispatcher.js","sourceRoot":"","sources":["ErrorDispatcher.ts"],"names":[],"mappings":";;AAAA,IAAM,eAAe,GAAG;IACtB,aAAa,EAAE,EAAE;IAEjB,oBAAoB,YAAC,cAAc;QACjC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC1C,CAAC;IAED,UAAU,YAAC,YAAY;QACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,EAAE;YAChD,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;IACxC,CAAC;CACF,CAAC;AAEF,kBAAe,eAAe,CAAC"}
|
@ -1,14 +0,0 @@
|
||||
const ErrorDispatcher = {
|
||||
errorHandlers: [],
|
||||
|
||||
registerErrorHandler(errorHandlerFn) {
|
||||
this.errorHandlers.push(errorHandlerFn);
|
||||
},
|
||||
|
||||
raiseError(errorMessage) {
|
||||
for (let i = 0; i < this.errorHandlers.length; i++)
|
||||
this.errorHandlers[i](errorMessage);
|
||||
}
|
||||
};
|
||||
|
||||
export default ErrorDispatcher;
|
@ -1,50 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var ErrorDispatcher_1 = require("./ErrorDispatcher");
|
||||
function get(url) {
|
||||
var options = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
return makeRequest(url, options);
|
||||
}
|
||||
function post(url, request) {
|
||||
var options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
},
|
||||
body: "",
|
||||
};
|
||||
if (request)
|
||||
options.body = JSON.stringify(request);
|
||||
return makeRequest(url, options);
|
||||
}
|
||||
function makeRequest(url, options) {
|
||||
return fetch(url, options)
|
||||
.then(ensureResponseSuccess)
|
||||
.catch(errorHandler);
|
||||
}
|
||||
function ensureResponseSuccess(response) {
|
||||
if (!response.ok)
|
||||
return response.json()
|
||||
.then(function (data) { return errorHandler(data); });
|
||||
return response.text()
|
||||
.then(function (text) { return text.length ? JSON.parse(text) : {}; });
|
||||
}
|
||||
function errorHandler(response) {
|
||||
console.log(response);
|
||||
if (response && response.Error)
|
||||
ErrorDispatcher_1.default.raiseError(response.Error);
|
||||
return Promise.reject();
|
||||
}
|
||||
exports.default = {
|
||||
get: get,
|
||||
post: post,
|
||||
makeRequest: makeRequest
|
||||
};
|
||||
//# sourceMappingURL=ServiceBase.js.map
|
@ -1 +0,0 @@
|
||||
{"version":3,"file":"ServiceBase.js","sourceRoot":"","sources":["ServiceBase.ts"],"names":[],"mappings":";;AAAA,qDAAgD;AAEhD,SAAS,GAAG,CAAC,GAAW;IACpB,IAAI,OAAO,GAAG;QACV,MAAM,EAAE,KAAK;QACb,OAAO,EAAE;YACL,cAAc,EAAE,kBAAkB;YAClC,eAAe,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC;SAClD;KACJ,CAAC;IAEF,OAAO,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,IAAI,CAAC,GAAW,EAAE,OAAY;IACnC,IAAI,OAAO,GAAG;QACV,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACL,cAAc,EAAE,kBAAkB;YAClC,eAAe,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC;SAClD;QACD,IAAI,EAAE,EAAE;KACX,CAAC;IAEF,IAAI,OAAO;QACP,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAE3C,OAAO,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,WAAW,CAAC,GAAW,EAAE,OAAY;IAC1C,OAAO,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC;SACrB,IAAI,CAAC,qBAAqB,CAAC;SAC3B,KAAK,CAAC,YAAY,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,qBAAqB,CAAC,QAAa;IACxC,IAAI,CAAC,QAAQ,CAAC,EAAE;QACZ,OAAO,QAAQ,CAAC,IAAI,EAAE;aACjB,IAAI,CAAC,UAAC,IAAS,IAAK,OAAA,YAAY,CAAC,IAAI,CAAC,EAAlB,CAAkB,CAAC,CAAC;IAEjD,OAAO,QAAQ,CAAC,IAAI,EAAE;SACjB,IAAI,CAAC,UAAC,IAAS,IAAK,OAAA,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,EAAnC,CAAmC,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,YAAY,CAAC,QAAa;IAC/B,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAEtB,IAAI,QAAQ,IAAI,QAAQ,CAAC,KAAK;QAC1B,yBAAe,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE/C,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC;AAC5B,CAAC;AAED,kBAAe;IACX,GAAG,KAAA;IACH,IAAI,MAAA;IACJ,WAAW,aAAA;CACd,CAAC"}
|
@ -1,59 +0,0 @@
|
||||
import ErrorDispatcher from './ErrorDispatcher';
|
||||
|
||||
function get(url: string) {
|
||||
let options = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
|
||||
return makeRequest(url, options);
|
||||
}
|
||||
|
||||
function post(url: string, request: any) {
|
||||
let options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
},
|
||||
body: "",
|
||||
};
|
||||
|
||||
if (request)
|
||||
options.body = JSON.stringify(request);
|
||||
|
||||
return makeRequest(url, options);
|
||||
}
|
||||
|
||||
function makeRequest(url: string, options: any) {
|
||||
return fetch(url, options)
|
||||
.then(ensureResponseSuccess)
|
||||
.catch(errorHandler);
|
||||
}
|
||||
|
||||
function ensureResponseSuccess(response: any) {
|
||||
if (!response.ok)
|
||||
return response.json()
|
||||
.then((data: any) => errorHandler(data));
|
||||
|
||||
return response.text()
|
||||
.then((text: any) => text.length ? JSON.parse(text) : {});
|
||||
}
|
||||
|
||||
function errorHandler(response: any) {
|
||||
console.log(response);
|
||||
|
||||
if (response && response.Error)
|
||||
ErrorDispatcher.raiseError(response.Error);
|
||||
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
export default {
|
||||
get,
|
||||
post,
|
||||
makeRequest
|
||||
};
|
136
Birdmap.API/ClientApp/src/components/appBar/BirdmapBar.tsx
Normal file
136
Birdmap.API/ClientApp/src/components/appBar/BirdmapBar.tsx
Normal file
@ -0,0 +1,136 @@
|
||||
import { ClickAwayListener, Container, createStyles, Grow, IconButton, makeStyles, MenuItem, MenuList, Paper, Popper, Theme } from '@material-ui/core';
|
||||
import AppBar from '@material-ui/core/AppBar';
|
||||
import Toolbar from '@material-ui/core/Toolbar';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import AccountCircle from '@material-ui/icons/AccountCircle';
|
||||
import React from 'react';
|
||||
import { Link, NavLink } from 'react-router-dom';
|
||||
import BirdmapTitle from './BirdmapTitle';
|
||||
|
||||
export default function BirdmapBar(props: { onLogout: () => void; isAuthenticated: any; isAdmin: any; }) {
|
||||
const classes = useAppbarStyles();
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const anchorRef = React.useRef<HTMLButtonElement>(null);
|
||||
|
||||
const handleToggle = () => {
|
||||
setOpen((prevOpen) => !prevOpen);
|
||||
};
|
||||
|
||||
const handleClose = (event: React.MouseEvent<EventTarget>) => {
|
||||
if (anchorRef.current && anchorRef.current.contains(event.target as HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const handleLogout = (event: React.MouseEvent<EventTarget>) => {
|
||||
if (anchorRef.current && anchorRef.current.contains(event.target as HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
props.onLogout();
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
function handleListKeyDown(event: React.KeyboardEvent) {
|
||||
if (event.key === 'Tab') {
|
||||
event.preventDefault();
|
||||
setOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
const prevOpen = React.useRef(open);
|
||||
React.useEffect(() => {
|
||||
if (prevOpen.current === true && open === false) {
|
||||
anchorRef.current!.focus();
|
||||
}
|
||||
|
||||
prevOpen.current = open;
|
||||
}, [open]);
|
||||
|
||||
const renderNavLinks = () => {
|
||||
return props.isAuthenticated
|
||||
? <Container className={classes.nav_menu}>
|
||||
<NavLink exact to="/" className={classes.nav_menu_item} activeClassName={classes.nav_menu_item_active}>Dashboard</NavLink>
|
||||
{props.isAdmin ? <NavLink exact to="/logs" className={classes.nav_menu_item} activeClassName={classes.nav_menu_item_active}>Logs</NavLink> : null}
|
||||
<NavLink to="/devices" className={classes.nav_menu_item} activeClassName={classes.nav_menu_item_active}>Devices</NavLink>
|
||||
<NavLink exact to="/heatmap" className={classes.nav_menu_item} activeClassName={classes.nav_menu_item_active}>Heatmap</NavLink>
|
||||
<IconButton className={classes.nav_menu_icon}
|
||||
ref={anchorRef}
|
||||
aria-haspopup="true"
|
||||
aria-controls={open ? 'menu-list-grow' : undefined}
|
||||
aria-label="account of current user"
|
||||
onClick={handleToggle}>
|
||||
<AccountCircle />
|
||||
</IconButton>
|
||||
<Popper open={open} anchorEl={anchorRef.current} role={undefined} transition disablePortal>
|
||||
{({ TransitionProps, placement }) => (
|
||||
<Grow
|
||||
{...TransitionProps}
|
||||
style={{ transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom' }}>
|
||||
<Paper>
|
||||
<ClickAwayListener onClickAway={handleClose}>
|
||||
<MenuList autoFocusItem={open} id="menu-list-grow" onKeyDown={handleListKeyDown}>
|
||||
<MenuItem onClick={handleLogout} component={Link} {...{ to: '/login' }}>Logout</MenuItem>
|
||||
</MenuList>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Grow>
|
||||
)}
|
||||
</Popper>
|
||||
</Container>
|
||||
: null;
|
||||
};
|
||||
|
||||
return (
|
||||
<AppBar position="static">
|
||||
<Toolbar>
|
||||
<BirdmapTitle />
|
||||
<Typography component={'span'} className={classes.typo}>
|
||||
{renderNavLinks()}
|
||||
</Typography>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
)
|
||||
};
|
||||
|
||||
|
||||
const useAppbarStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
typo: {
|
||||
marginLeft: 'auto',
|
||||
color: 'white',
|
||||
},
|
||||
nav_menu: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
nav_menu_icon: {
|
||||
color: 'inherit',
|
||||
marginLeft: '24px',
|
||||
'&:hover': {
|
||||
color: 'inherit',
|
||||
}
|
||||
},
|
||||
nav_menu_item: {
|
||||
textDecoration: 'none',
|
||||
fontWeight: 'normal',
|
||||
color: 'inherit',
|
||||
marginLeft: '24px',
|
||||
'&:hover': {
|
||||
color: 'inherit',
|
||||
}
|
||||
},
|
||||
nav_menu_item_active: {
|
||||
textDecoration: 'underline',
|
||||
fontWeight: 'bold',
|
||||
color: 'inherit',
|
||||
marginLeft: '24px',
|
||||
'&:hover': {
|
||||
color: 'inherit',
|
||||
}
|
||||
},
|
||||
}),
|
||||
);
|
@ -55,13 +55,13 @@ export default function Auth(props: any) {
|
||||
setIsLoggingIn(true);
|
||||
AuthService.login(username, password)
|
||||
.then(() => {
|
||||
setIsLoggingIn(false);
|
||||
props.onAuthenticated();
|
||||
history.push('/');
|
||||
}).catch(() => {
|
||||
setShowError(true);
|
||||
setErrorMessage('Invalid credentials');
|
||||
}).finally(() => {
|
||||
setIsLoggingIn(false);
|
||||
setErrorMessage('Invalid credentials');
|
||||
});
|
||||
};
|
||||
|
||||
|
256
Birdmap.API/ClientApp/src/components/auth/AuthClient.js
Normal file
256
Birdmap.API/ClientApp/src/components/auth/AuthClient.js
Normal file
@ -0,0 +1,256 @@
|
||||
"use strict";
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = function (d, b) {
|
||||
extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
||||
return extendStatics(d, b);
|
||||
};
|
||||
return function (d, b) {
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ApiException = exports.RegisterRequest = exports.AuthenticateRequest = exports.HttpStatusCode = void 0;
|
||||
var AuthClient = /** @class */ (function () {
|
||||
function AuthClient(baseUrl, http) {
|
||||
this.jsonParseReviver = undefined;
|
||||
this.http = http ? http : window;
|
||||
this.baseUrl = baseUrl !== undefined && baseUrl !== null ? baseUrl : "";
|
||||
}
|
||||
AuthClient.prototype.authenticate = function (model) {
|
||||
var _this = this;
|
||||
var url_ = this.baseUrl + "/api/Auth/authenticate";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
var content_ = JSON.stringify(model);
|
||||
var options_ = {
|
||||
body: content_,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json"
|
||||
}
|
||||
};
|
||||
return this.http.fetch(url_, options_).then(function (_response) {
|
||||
return _this.processAuthenticate(_response);
|
||||
});
|
||||
};
|
||||
AuthClient.prototype.processAuthenticate = function (response) {
|
||||
var _this = this;
|
||||
var status = response.status;
|
||||
var _headers = {};
|
||||
if (response.headers && response.headers.forEach) {
|
||||
response.headers.forEach(function (v, k) { return _headers[k] = v; });
|
||||
}
|
||||
;
|
||||
if (status === 200) {
|
||||
return response.text().then(function (_responseText) {
|
||||
var result200 = null;
|
||||
var resultData200 = _responseText === "" ? null : JSON.parse(_responseText, _this.jsonParseReviver);
|
||||
result200 = resultData200 !== undefined ? resultData200 : null;
|
||||
return result200;
|
||||
});
|
||||
}
|
||||
else if (status !== 200 && status !== 204) {
|
||||
return response.text().then(function (_responseText) {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
};
|
||||
AuthClient.prototype.register = function (model) {
|
||||
var _this = this;
|
||||
var url_ = this.baseUrl + "/api/Auth/register";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
var content_ = JSON.stringify(model);
|
||||
var options_ = {
|
||||
body: content_,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
};
|
||||
return this.http.fetch(url_, options_).then(function (_response) {
|
||||
return _this.processRegister(_response);
|
||||
});
|
||||
};
|
||||
AuthClient.prototype.processRegister = function (response) {
|
||||
var status = response.status;
|
||||
var _headers = {};
|
||||
if (response.headers && response.headers.forEach) {
|
||||
response.headers.forEach(function (v, k) { return _headers[k] = v; });
|
||||
}
|
||||
;
|
||||
if (status === 204) {
|
||||
return response.text().then(function (_responseText) {
|
||||
return;
|
||||
});
|
||||
}
|
||||
else if (status !== 200 && status !== 204) {
|
||||
return response.text().then(function (_responseText) {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
};
|
||||
return AuthClient;
|
||||
}());
|
||||
exports.default = AuthClient;
|
||||
var HttpStatusCode;
|
||||
(function (HttpStatusCode) {
|
||||
HttpStatusCode["Continue"] = "Continue";
|
||||
HttpStatusCode["SwitchingProtocols"] = "SwitchingProtocols";
|
||||
HttpStatusCode["Processing"] = "Processing";
|
||||
HttpStatusCode["EarlyHints"] = "EarlyHints";
|
||||
HttpStatusCode["OK"] = "OK";
|
||||
HttpStatusCode["Created"] = "Created";
|
||||
HttpStatusCode["Accepted"] = "Accepted";
|
||||
HttpStatusCode["NonAuthoritativeInformation"] = "NonAuthoritativeInformation";
|
||||
HttpStatusCode["NoContent"] = "NoContent";
|
||||
HttpStatusCode["ResetContent"] = "ResetContent";
|
||||
HttpStatusCode["PartialContent"] = "PartialContent";
|
||||
HttpStatusCode["MultiStatus"] = "MultiStatus";
|
||||
HttpStatusCode["AlreadyReported"] = "AlreadyReported";
|
||||
HttpStatusCode["IMUsed"] = "IMUsed";
|
||||
HttpStatusCode["MultipleChoices"] = "Ambiguous";
|
||||
HttpStatusCode["Ambiguous"] = "Ambiguous";
|
||||
HttpStatusCode["MovedPermanently"] = "Moved";
|
||||
HttpStatusCode["Moved"] = "Moved";
|
||||
HttpStatusCode["Found"] = "Redirect";
|
||||
HttpStatusCode["Redirect"] = "Redirect";
|
||||
HttpStatusCode["SeeOther"] = "RedirectMethod";
|
||||
HttpStatusCode["RedirectMethod"] = "RedirectMethod";
|
||||
HttpStatusCode["NotModified"] = "NotModified";
|
||||
HttpStatusCode["UseProxy"] = "UseProxy";
|
||||
HttpStatusCode["Unused"] = "Unused";
|
||||
HttpStatusCode["TemporaryRedirect"] = "TemporaryRedirect";
|
||||
HttpStatusCode["RedirectKeepVerb"] = "TemporaryRedirect";
|
||||
HttpStatusCode["PermanentRedirect"] = "PermanentRedirect";
|
||||
HttpStatusCode["BadRequest"] = "BadRequest";
|
||||
HttpStatusCode["Unauthorized"] = "Unauthorized";
|
||||
HttpStatusCode["PaymentRequired"] = "PaymentRequired";
|
||||
HttpStatusCode["Forbidden"] = "Forbidden";
|
||||
HttpStatusCode["NotFound"] = "NotFound";
|
||||
HttpStatusCode["MethodNotAllowed"] = "MethodNotAllowed";
|
||||
HttpStatusCode["NotAcceptable"] = "NotAcceptable";
|
||||
HttpStatusCode["ProxyAuthenticationRequired"] = "ProxyAuthenticationRequired";
|
||||
HttpStatusCode["RequestTimeout"] = "RequestTimeout";
|
||||
HttpStatusCode["Conflict"] = "Conflict";
|
||||
HttpStatusCode["Gone"] = "Gone";
|
||||
HttpStatusCode["LengthRequired"] = "LengthRequired";
|
||||
HttpStatusCode["PreconditionFailed"] = "PreconditionFailed";
|
||||
HttpStatusCode["RequestEntityTooLarge"] = "RequestEntityTooLarge";
|
||||
HttpStatusCode["RequestUriTooLong"] = "RequestUriTooLong";
|
||||
HttpStatusCode["UnsupportedMediaType"] = "UnsupportedMediaType";
|
||||
HttpStatusCode["RequestedRangeNotSatisfiable"] = "RequestedRangeNotSatisfiable";
|
||||
HttpStatusCode["ExpectationFailed"] = "ExpectationFailed";
|
||||
HttpStatusCode["MisdirectedRequest"] = "MisdirectedRequest";
|
||||
HttpStatusCode["UnprocessableEntity"] = "UnprocessableEntity";
|
||||
HttpStatusCode["Locked"] = "Locked";
|
||||
HttpStatusCode["FailedDependency"] = "FailedDependency";
|
||||
HttpStatusCode["UpgradeRequired"] = "UpgradeRequired";
|
||||
HttpStatusCode["PreconditionRequired"] = "PreconditionRequired";
|
||||
HttpStatusCode["TooManyRequests"] = "TooManyRequests";
|
||||
HttpStatusCode["RequestHeaderFieldsTooLarge"] = "RequestHeaderFieldsTooLarge";
|
||||
HttpStatusCode["UnavailableForLegalReasons"] = "UnavailableForLegalReasons";
|
||||
HttpStatusCode["InternalServerError"] = "InternalServerError";
|
||||
HttpStatusCode["NotImplemented"] = "NotImplemented";
|
||||
HttpStatusCode["BadGateway"] = "BadGateway";
|
||||
HttpStatusCode["ServiceUnavailable"] = "ServiceUnavailable";
|
||||
HttpStatusCode["GatewayTimeout"] = "GatewayTimeout";
|
||||
HttpStatusCode["HttpVersionNotSupported"] = "HttpVersionNotSupported";
|
||||
HttpStatusCode["VariantAlsoNegotiates"] = "VariantAlsoNegotiates";
|
||||
HttpStatusCode["InsufficientStorage"] = "InsufficientStorage";
|
||||
HttpStatusCode["LoopDetected"] = "LoopDetected";
|
||||
HttpStatusCode["NotExtended"] = "NotExtended";
|
||||
HttpStatusCode["NetworkAuthenticationRequired"] = "NetworkAuthenticationRequired";
|
||||
})(HttpStatusCode = exports.HttpStatusCode || (exports.HttpStatusCode = {}));
|
||||
var AuthenticateRequest = /** @class */ (function () {
|
||||
function AuthenticateRequest(data) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
this[property] = data[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
AuthenticateRequest.prototype.init = function (_data) {
|
||||
if (_data) {
|
||||
this.username = _data["username"];
|
||||
this.password = _data["password"];
|
||||
}
|
||||
};
|
||||
AuthenticateRequest.fromJS = function (data) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
var result = new AuthenticateRequest();
|
||||
result.init(data);
|
||||
return result;
|
||||
};
|
||||
AuthenticateRequest.prototype.toJSON = function (data) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
data["username"] = this.username;
|
||||
data["password"] = this.password;
|
||||
return data;
|
||||
};
|
||||
return AuthenticateRequest;
|
||||
}());
|
||||
exports.AuthenticateRequest = AuthenticateRequest;
|
||||
var RegisterRequest = /** @class */ (function () {
|
||||
function RegisterRequest(data) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
this[property] = data[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
RegisterRequest.prototype.init = function (_data) {
|
||||
if (_data) {
|
||||
this.username = _data["username"];
|
||||
this.password = _data["password"];
|
||||
this.confirmPassword = _data["confirmPassword"];
|
||||
}
|
||||
};
|
||||
RegisterRequest.fromJS = function (data) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
var result = new RegisterRequest();
|
||||
result.init(data);
|
||||
return result;
|
||||
};
|
||||
RegisterRequest.prototype.toJSON = function (data) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
data["username"] = this.username;
|
||||
data["password"] = this.password;
|
||||
data["confirmPassword"] = this.confirmPassword;
|
||||
return data;
|
||||
};
|
||||
return RegisterRequest;
|
||||
}());
|
||||
exports.RegisterRequest = RegisterRequest;
|
||||
var ApiException = /** @class */ (function (_super) {
|
||||
__extends(ApiException, _super);
|
||||
function ApiException(message, status, response, headers, result) {
|
||||
var _this = _super.call(this) || this;
|
||||
_this.isApiException = true;
|
||||
_this.message = message;
|
||||
_this.status = status;
|
||||
_this.response = response;
|
||||
_this.headers = headers;
|
||||
_this.result = result;
|
||||
return _this;
|
||||
}
|
||||
ApiException.isApiException = function (obj) {
|
||||
return obj.isApiException === true;
|
||||
};
|
||||
return ApiException;
|
||||
}(Error));
|
||||
exports.ApiException = ApiException;
|
||||
function throwException(message, status, response, headers, result) {
|
||||
if (result !== null && result !== undefined)
|
||||
throw result;
|
||||
else
|
||||
throw new ApiException(message, status, response, headers, null);
|
||||
}
|
||||
//# sourceMappingURL=AuthClient.js.map
|
File diff suppressed because one or more lines are too long
274
Birdmap.API/ClientApp/src/components/auth/AuthClient.ts
Normal file
274
Birdmap.API/ClientApp/src/components/auth/AuthClient.ts
Normal file
@ -0,0 +1,274 @@
|
||||
export default class AuthClient {
|
||||
private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
|
||||
private baseUrl: string;
|
||||
protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
|
||||
|
||||
constructor(baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> }) {
|
||||
this.http = http ? http : <any>window;
|
||||
this.baseUrl = baseUrl !== undefined && baseUrl !== null ? baseUrl : "";
|
||||
}
|
||||
|
||||
authenticate(model: AuthenticateRequest): Promise<any> {
|
||||
let url_ = this.baseUrl + "/api/Auth/authenticate";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
const content_ = JSON.stringify(model);
|
||||
|
||||
let options_ = <RequestInit>{
|
||||
body: content_,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json"
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processAuthenticate(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processAuthenticate(response: Response): Promise<any> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 200) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result200: any = null;
|
||||
let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
result200 = resultData200 !== undefined ? resultData200 : <any>null;
|
||||
return result200;
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<any>(<any>null);
|
||||
}
|
||||
|
||||
register(model: RegisterRequest): Promise<void> {
|
||||
let url_ = this.baseUrl + "/api/Auth/register";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
const content_ = JSON.stringify(model);
|
||||
|
||||
let options_ = <RequestInit>{
|
||||
body: content_,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processRegister(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processRegister(response: Response): Promise<void> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return;
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<void>(<any>null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export enum HttpStatusCode {
|
||||
Continue = "Continue",
|
||||
SwitchingProtocols = "SwitchingProtocols",
|
||||
Processing = "Processing",
|
||||
EarlyHints = "EarlyHints",
|
||||
OK = "OK",
|
||||
Created = "Created",
|
||||
Accepted = "Accepted",
|
||||
NonAuthoritativeInformation = "NonAuthoritativeInformation",
|
||||
NoContent = "NoContent",
|
||||
ResetContent = "ResetContent",
|
||||
PartialContent = "PartialContent",
|
||||
MultiStatus = "MultiStatus",
|
||||
AlreadyReported = "AlreadyReported",
|
||||
IMUsed = "IMUsed",
|
||||
MultipleChoices = "Ambiguous",
|
||||
Ambiguous = "Ambiguous",
|
||||
MovedPermanently = "Moved",
|
||||
Moved = "Moved",
|
||||
Found = "Redirect",
|
||||
Redirect = "Redirect",
|
||||
SeeOther = "RedirectMethod",
|
||||
RedirectMethod = "RedirectMethod",
|
||||
NotModified = "NotModified",
|
||||
UseProxy = "UseProxy",
|
||||
Unused = "Unused",
|
||||
TemporaryRedirect = "TemporaryRedirect",
|
||||
RedirectKeepVerb = "TemporaryRedirect",
|
||||
PermanentRedirect = "PermanentRedirect",
|
||||
BadRequest = "BadRequest",
|
||||
Unauthorized = "Unauthorized",
|
||||
PaymentRequired = "PaymentRequired",
|
||||
Forbidden = "Forbidden",
|
||||
NotFound = "NotFound",
|
||||
MethodNotAllowed = "MethodNotAllowed",
|
||||
NotAcceptable = "NotAcceptable",
|
||||
ProxyAuthenticationRequired = "ProxyAuthenticationRequired",
|
||||
RequestTimeout = "RequestTimeout",
|
||||
Conflict = "Conflict",
|
||||
Gone = "Gone",
|
||||
LengthRequired = "LengthRequired",
|
||||
PreconditionFailed = "PreconditionFailed",
|
||||
RequestEntityTooLarge = "RequestEntityTooLarge",
|
||||
RequestUriTooLong = "RequestUriTooLong",
|
||||
UnsupportedMediaType = "UnsupportedMediaType",
|
||||
RequestedRangeNotSatisfiable = "RequestedRangeNotSatisfiable",
|
||||
ExpectationFailed = "ExpectationFailed",
|
||||
MisdirectedRequest = "MisdirectedRequest",
|
||||
UnprocessableEntity = "UnprocessableEntity",
|
||||
Locked = "Locked",
|
||||
FailedDependency = "FailedDependency",
|
||||
UpgradeRequired = "UpgradeRequired",
|
||||
PreconditionRequired = "PreconditionRequired",
|
||||
TooManyRequests = "TooManyRequests",
|
||||
RequestHeaderFieldsTooLarge = "RequestHeaderFieldsTooLarge",
|
||||
UnavailableForLegalReasons = "UnavailableForLegalReasons",
|
||||
InternalServerError = "InternalServerError",
|
||||
NotImplemented = "NotImplemented",
|
||||
BadGateway = "BadGateway",
|
||||
ServiceUnavailable = "ServiceUnavailable",
|
||||
GatewayTimeout = "GatewayTimeout",
|
||||
HttpVersionNotSupported = "HttpVersionNotSupported",
|
||||
VariantAlsoNegotiates = "VariantAlsoNegotiates",
|
||||
InsufficientStorage = "InsufficientStorage",
|
||||
LoopDetected = "LoopDetected",
|
||||
NotExtended = "NotExtended",
|
||||
NetworkAuthenticationRequired = "NetworkAuthenticationRequired",
|
||||
}
|
||||
|
||||
export class AuthenticateRequest implements IAuthenticateRequest {
|
||||
username!: string;
|
||||
password!: string;
|
||||
|
||||
constructor(data?: IAuthenticateRequest) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
(<any>this)[property] = (<any>data)[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(_data?: any) {
|
||||
if (_data) {
|
||||
this.username = _data["username"];
|
||||
this.password = _data["password"];
|
||||
}
|
||||
}
|
||||
|
||||
static fromJS(data: any): AuthenticateRequest {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
let result = new AuthenticateRequest();
|
||||
result.init(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON(data?: any) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
data["username"] = this.username;
|
||||
data["password"] = this.password;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IAuthenticateRequest {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export class RegisterRequest implements IRegisterRequest {
|
||||
username!: string;
|
||||
password!: string;
|
||||
confirmPassword!: string;
|
||||
|
||||
constructor(data?: IRegisterRequest) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
(<any>this)[property] = (<any>data)[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(_data?: any) {
|
||||
if (_data) {
|
||||
this.username = _data["username"];
|
||||
this.password = _data["password"];
|
||||
this.confirmPassword = _data["confirmPassword"];
|
||||
}
|
||||
}
|
||||
|
||||
static fromJS(data: any): RegisterRequest {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
let result = new RegisterRequest();
|
||||
result.init(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON(data?: any) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
data["username"] = this.username;
|
||||
data["password"] = this.password;
|
||||
data["confirmPassword"] = this.confirmPassword;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IRegisterRequest {
|
||||
username: string;
|
||||
password: string;
|
||||
confirmPassword: string;
|
||||
}
|
||||
|
||||
export interface FileResponse {
|
||||
data: Blob;
|
||||
status: number;
|
||||
fileName?: string;
|
||||
headers?: { [name: string]: any };
|
||||
}
|
||||
|
||||
export class ApiException extends Error {
|
||||
message: string;
|
||||
status: number;
|
||||
response: string;
|
||||
headers: { [key: string]: any; };
|
||||
result: any;
|
||||
|
||||
constructor(message: string, status: number, response: string, headers: { [key: string]: any; }, result: any) {
|
||||
super();
|
||||
|
||||
this.message = message;
|
||||
this.status = status;
|
||||
this.response = response;
|
||||
this.headers = headers;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
protected isApiException = true;
|
||||
|
||||
static isApiException(obj: any): obj is ApiException {
|
||||
return obj.isApiException === true;
|
||||
}
|
||||
}
|
||||
|
||||
function throwException(message: string, status: number, response: string, headers: { [key: string]: any; }, result?: any): any {
|
||||
if (result !== null && result !== undefined)
|
||||
throw result;
|
||||
else
|
||||
throw new ApiException(message, status, response, headers, null);
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var ServiceBase_1 = require("../../common/ServiceBase");
|
||||
var login_url = 'api/auth/authenticate';
|
||||
var AuthClient_1 = require("./AuthClient");
|
||||
exports.default = {
|
||||
isAuthenticated: function () {
|
||||
return sessionStorage.getItem('user') !== null;
|
||||
return sessionStorage.getItem('user') !== null && sessionStorage.getItem('user') !== undefined;
|
||||
},
|
||||
isAdmin: function () {
|
||||
return sessionStorage.getItem('role') === 'Admin';
|
||||
@ -14,21 +13,16 @@ exports.default = {
|
||||
sessionStorage.removeItem('role');
|
||||
},
|
||||
login: function (username, password) {
|
||||
var body = {
|
||||
var service = new AuthClient_1.default();
|
||||
var request = new AuthClient_1.AuthenticateRequest({
|
||||
username: username,
|
||||
password: password
|
||||
};
|
||||
var options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
return ServiceBase_1.default.makeRequest(login_url, options)
|
||||
});
|
||||
return service.authenticate(request)
|
||||
.then(function (response) {
|
||||
sessionStorage.setItem('user', response.token_type + " " + response.access_token);
|
||||
sessionStorage.setItem('role', response.role);
|
||||
//console.log(response);
|
||||
sessionStorage.setItem('user', response.tokenType + " " + response.accessToken);
|
||||
sessionStorage.setItem('role', response.userRole);
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
{"version":3,"file":"AuthService.js","sourceRoot":"","sources":["AuthService.ts"],"names":[],"mappings":";;AAAA,wDAAmD;AAEnD,IAAM,SAAS,GAAG,uBAAuB,CAAC;AAE1C,kBAAe;IACX,eAAe;QACX,OAAO,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IACnD,CAAC;IAED,OAAO;QACH,OAAO,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC;IACtD,CAAC;IAED,MAAM;QACF,cAAc,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAClC,cAAc,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,EAAL,UAAM,QAAgB,EAAE,QAAgB;QACpC,IAAI,IAAI,GAAG;YACP,QAAQ,EAAE,QAAQ;YAClB,QAAQ,EAAE,QAAQ;SACrB,CAAC;QACF,IAAI,OAAO,GAAG;YACV,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACL,cAAc,EAAE,kBAAkB;aACrC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;SAC7B,CAAC;QAEF,OAAO,qBAAW,CAAC,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC;aAC7C,IAAI,CAAC,UAAA,QAAQ;YACV,cAAc,CAAC,OAAO,CAAC,MAAM,EAAK,QAAQ,CAAC,UAAU,SAAI,QAAQ,CAAC,YAAc,CAAC,CAAC;YAClF,cAAc,CAAC,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;YAC9C,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC7B,CAAC,CAAC,CAAC;IACX,CAAC;CACJ,CAAA"}
|
||||
{"version":3,"file":"AuthService.js","sourceRoot":"","sources":["AuthService.ts"],"names":[],"mappings":";;AAAA,2CAA+D;AAE/D,kBAAe;IACX,eAAe;QACX,OAAO,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,IAAI,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,SAAS,CAAC;IACnG,CAAC;IAED,OAAO;QACH,OAAO,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC;IACtD,CAAC;IAED,MAAM;QACF,cAAc,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAClC,cAAc,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,EAAL,UAAM,QAAgB,EAAE,QAAgB;QACpC,IAAM,OAAO,GAAG,IAAI,oBAAU,EAAE,CAAC;QAEjC,IAAI,OAAO,GAAG,IAAI,gCAAmB,CAAC;YAClC,QAAQ,EAAE,QAAQ;YAClB,QAAQ,EAAE,QAAQ;SACrB,CAAC,CAAC;QAEH,OAAO,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC;aAC/B,IAAI,CAAC,UAAA,QAAQ;YACV,wBAAwB;YACxB,cAAc,CAAC,OAAO,CAAC,MAAM,EAAK,QAAQ,CAAC,SAAS,SAAI,QAAQ,CAAC,WAAa,CAAC,CAAC;YAChF,cAAc,CAAC,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAClD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC7B,CAAC,CAAC,CAAC;IACX,CAAC;CACJ,CAAA"}
|
@ -1,10 +1,8 @@
|
||||
import ServiceBase from '../../common/ServiceBase';
|
||||
|
||||
const login_url = 'api/auth/authenticate';
|
||||
import AuthClient, { AuthenticateRequest } from './AuthClient';
|
||||
|
||||
export default {
|
||||
isAuthenticated() {
|
||||
return sessionStorage.getItem('user') !== null;
|
||||
return sessionStorage.getItem('user') !== null && sessionStorage.getItem('user') !== undefined;
|
||||
},
|
||||
|
||||
isAdmin() {
|
||||
@ -17,22 +15,18 @@ export default {
|
||||
},
|
||||
|
||||
login(username: string, password: string) {
|
||||
let body = {
|
||||
const service = new AuthClient();
|
||||
|
||||
let request = new AuthenticateRequest({
|
||||
username: username,
|
||||
password: password
|
||||
};
|
||||
let options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
});
|
||||
|
||||
return ServiceBase.makeRequest(login_url, options)
|
||||
return service.authenticate(request)
|
||||
.then(response => {
|
||||
sessionStorage.setItem('user', `${response.token_type} ${response.access_token}`);
|
||||
sessionStorage.setItem('role', response.role);
|
||||
//console.log(response);
|
||||
sessionStorage.setItem('user', `${response.tokenType} ${response.accessToken}`);
|
||||
sessionStorage.setItem('role', response.userRole);
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
|
328
Birdmap.API/ClientApp/src/components/dashboard/Dashboard.jsx
Normal file
328
Birdmap.API/ClientApp/src/components/dashboard/Dashboard.jsx
Normal file
@ -0,0 +1,328 @@
|
||||
import React, { Component } from 'react';
|
||||
import { withStyles } from '@material-ui/styles';
|
||||
import Services from './services/Services';
|
||||
import { blueGrey } from '@material-ui/core/colors';
|
||||
import { Box, Grid, IconButton, Paper, Typography } from '@material-ui/core';
|
||||
import DonutChart from './charts/DonutChart';
|
||||
import HeatmapChart from './charts/HeatmapChart';
|
||||
import BarChart from './charts/BarChart';
|
||||
import LineChart from './charts/LineChart';
|
||||
import DevicesContext from '../../contexts/DevicesContext';
|
||||
import C from '../../common/Constants';
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
flexGrow: 1,
|
||||
padding: '64px',
|
||||
backgroundColor: theme.palette.primary.dark,
|
||||
},
|
||||
typo: {
|
||||
fontSize: theme.typography.pxToRem(20),
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
},
|
||||
paper: {
|
||||
backgroundColor: blueGrey[50],
|
||||
padding: '16px',
|
||||
}
|
||||
});
|
||||
|
||||
class Dashboard extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
deviceSeries: [],
|
||||
sensorSeries: [],
|
||||
heatmapSecondsSeries: [],
|
||||
heatmapMinutesSeries: [],
|
||||
barSeries: [],
|
||||
barCategories: [],
|
||||
lineSeries: [],
|
||||
};
|
||||
|
||||
this.updateSeries = this.updateSeries.bind(this);
|
||||
this.updateDynamic = this.updateDynamic.bind(this);
|
||||
this.performTask = this.performTask.bind(this);
|
||||
}
|
||||
|
||||
static contextType = DevicesContext;
|
||||
|
||||
componentDidMount() {
|
||||
this.context.addHandler(C.update_all_method_name, this.updateSeries);
|
||||
this.context.addHandler(C.update_method_name, this.updateSeries);
|
||||
this.updateSeries();
|
||||
this.updateDynamic();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.context.removeHandler(C.update_all_method_name, this.updateSeries);
|
||||
this.context.removeHandler(C.update_method_name, this.updateSeries);
|
||||
if (this.updateTimer) {
|
||||
clearTimeout(this.updateTimer);
|
||||
}
|
||||
}
|
||||
|
||||
getItemsWithStatus(iterate, status) {
|
||||
const items = [];
|
||||
|
||||
for (var d of iterate) {
|
||||
if (d.status == status) {
|
||||
items.push(d);
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
getDevicesWithStatus(status) {
|
||||
return this.getItemsWithStatus(this.context.devices, status);
|
||||
}
|
||||
|
||||
getSensorsWithStatus(status) {
|
||||
const sensors = [];
|
||||
|
||||
for (var d of this.context.devices) {
|
||||
sensors.push(...d.sensors)
|
||||
}
|
||||
|
||||
return this.getItemsWithStatus(sensors, status);
|
||||
}
|
||||
|
||||
getDeviceSeries() {
|
||||
var online = this.getDevicesWithStatus("Online").length;
|
||||
var offline = this.getDevicesWithStatus("Offline").length;
|
||||
var error = this.getDevicesWithStatus("Error").length;
|
||||
|
||||
return [online, offline, error]
|
||||
}
|
||||
|
||||
getSensorSeries() {
|
||||
var online = this.getSensorsWithStatus("Online").length;
|
||||
var offline = this.getSensorsWithStatus("Offline").length;
|
||||
var unknown = this.getSensorsWithStatus("Unknown").length;
|
||||
|
||||
return [online, offline, unknown]
|
||||
}
|
||||
|
||||
updateSeries() {
|
||||
this.setState({
|
||||
deviceSeries: this.getDeviceSeries(),
|
||||
sensorSeries: this.getSensorSeries()
|
||||
});
|
||||
}
|
||||
|
||||
updateDynamic = () => {
|
||||
const secondAgo = new Date();
|
||||
secondAgo.setMilliseconds(0);
|
||||
const minuteAgo = new Date(Date.now() - 1000 * 60);
|
||||
const hourAgo = new Date(Date.now() - 1000 * 60 * 60);
|
||||
|
||||
const minuteDevicePoints = {};
|
||||
const hourDevicePoints = {};
|
||||
const barDevicePoints = {};
|
||||
const linePoints = {};
|
||||
|
||||
for (var d of this.context.devices) {
|
||||
minuteDevicePoints[d.id] = Array(60).fill(0);
|
||||
hourDevicePoints[d.id] = Array(60).fill(0);
|
||||
barDevicePoints[d.id] = Array(3).fill(0);
|
||||
}
|
||||
|
||||
const processHeatmapItem = (items, index) => {
|
||||
const p = items[index];
|
||||
if (p.date > minuteAgo) {
|
||||
var seconds = Math.floor((p.date.getTime() - minuteAgo.getTime()) / 1000);
|
||||
var oldProb = minuteDevicePoints[p.deviceId][seconds];
|
||||
if (oldProb < p.prob) {
|
||||
minuteDevicePoints[p.deviceId][seconds] = p.prob;
|
||||
}
|
||||
}
|
||||
|
||||
if (p.date > hourAgo) {
|
||||
var minutes = Math.floor((p.date.getTime() - hourAgo.getTime()) / (1000 * 60));
|
||||
var oldProb = hourDevicePoints[p.deviceId][minutes];
|
||||
if (oldProb < p.prob) {
|
||||
hourDevicePoints[p.deviceId][minutes] = p.prob;
|
||||
}
|
||||
}
|
||||
|
||||
if (p.prob > 0.5 && p.prob <= 0.7) {
|
||||
barDevicePoints[p.deviceId][0] += 1;
|
||||
}
|
||||
if (p.prob > 0.7 && p.prob <= 0.9) {
|
||||
barDevicePoints[p.deviceId][1] += 1;
|
||||
}
|
||||
if (p.prob > 0.9) {
|
||||
barDevicePoints[p.deviceId][2] += 1;
|
||||
}
|
||||
|
||||
if (p.date < secondAgo) {
|
||||
var shortDate = p.date.toUTCString();
|
||||
var point = linePoints[shortDate];
|
||||
if (point === undefined) {
|
||||
linePoints[shortDate] = 1;
|
||||
} else {
|
||||
linePoints[shortDate] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onFinished = () => {
|
||||
const minuteHeatmapSeries = [];
|
||||
|
||||
var i = 0;
|
||||
for (var p in minuteDevicePoints) {
|
||||
minuteHeatmapSeries.push({
|
||||
name: "Device " + i,
|
||||
data: minuteDevicePoints[p].map((value, index) => ({
|
||||
x: new Date(Date.now() - (60 - index) * 1000).toLocaleTimeString('hu-HU'),
|
||||
y: value
|
||||
})),
|
||||
});
|
||||
i++;
|
||||
};
|
||||
|
||||
const hourHeatmapSeries = [];
|
||||
|
||||
var i = 0;
|
||||
for (var p in hourDevicePoints) {
|
||||
hourHeatmapSeries.push({
|
||||
name: "Device " + i,
|
||||
data: hourDevicePoints[p].map((value, index) => ({
|
||||
x: new Date(Date.now() - (60 - index) * 1000 * 60).toLocaleTimeString('hu-HU').substring(0, 5),
|
||||
y: value
|
||||
})),
|
||||
});
|
||||
i++;
|
||||
};
|
||||
|
||||
const barSeries = [];
|
||||
|
||||
const getCount = column => {
|
||||
var counts = [];
|
||||
|
||||
for (var p in barDevicePoints) {
|
||||
counts.unshift(barDevicePoints[p][column]);
|
||||
}
|
||||
|
||||
return counts;
|
||||
};
|
||||
|
||||
barSeries.push({
|
||||
name: "Prob > 0.5",
|
||||
data: getCount(0),
|
||||
});
|
||||
barSeries.push({
|
||||
name: "Prob > 0.7",
|
||||
data: getCount(1),
|
||||
});
|
||||
barSeries.push({
|
||||
name: "Prob > 0.9",
|
||||
data: getCount(2),
|
||||
});
|
||||
|
||||
const lineSeries = [{ name: "message/sec", data: [] }];
|
||||
for (var m in linePoints) {
|
||||
lineSeries[0].data.push({
|
||||
x: new Date(m).getTime(),
|
||||
y: linePoints[m],
|
||||
})
|
||||
}
|
||||
|
||||
const getBarCategories = () => {
|
||||
const categories = [];
|
||||
|
||||
for (var i = this.context.devices.length - 1; i >= 0; i--) {
|
||||
categories.push("Device " + i)
|
||||
}
|
||||
|
||||
return categories;
|
||||
}
|
||||
|
||||
const toUpdate = [
|
||||
{ heatmapSecondsSeries: minuteHeatmapSeries },
|
||||
{ heatmapMinutesSeries: hourHeatmapSeries },
|
||||
{ barSeries: barSeries },
|
||||
{ barCategories: getBarCategories() },
|
||||
{ lineSeries: lineSeries }
|
||||
];
|
||||
|
||||
//Set states must be done separately otherwise ApexChart's UI update freezes the page.
|
||||
this.performTask(toUpdate, 2, 300, (list, index) => {
|
||||
this.setState(list[index]);
|
||||
},
|
||||
() => {
|
||||
this.updateTimer = setTimeout(this.updateDynamic, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
this.performTask(this.context.heatmapPoints, Math.ceil(this.context.heatmapPoints.length / 50), 20,
|
||||
processHeatmapItem, onFinished);
|
||||
}
|
||||
|
||||
performTask(items, numToProcess, wait, processItem, onFinished) {
|
||||
var pos = 0;
|
||||
// This is run once for every numToProcess items.
|
||||
function iteration() {
|
||||
// Calculate last position.
|
||||
var j = Math.min(pos + numToProcess, items.length);
|
||||
// Start at current position and loop to last position.
|
||||
for (var i = pos; i < j; i++) {
|
||||
processItem(items, i);
|
||||
}
|
||||
// Increment current position.
|
||||
pos += numToProcess;
|
||||
// Only continue if there are more items to process.
|
||||
if (pos < items.length)
|
||||
setTimeout(iteration, wait); // Wait 10 ms to let the UI update.
|
||||
else
|
||||
onFinished();
|
||||
}
|
||||
iteration();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<Services isAdmin={this.props.isAdmin} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Paper className={classes.paper}>
|
||||
<DonutChart totalLabel="Devices" series={this.state.deviceSeries} />
|
||||
</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Paper className={classes.paper}>
|
||||
<DonutChart totalLabel="Sensors" series={this.state.sensorSeries} />
|
||||
</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Paper className={classes.paper}>
|
||||
<HeatmapChart label="Highest probability per second by devices" series={this.state.heatmapSecondsSeries} />
|
||||
</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Paper className={classes.paper}>
|
||||
<HeatmapChart label="Highest probability per minute by devices" series={this.state.heatmapMinutesSeries} />
|
||||
</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Paper className={classes.paper}>
|
||||
<BarChart label="# of messages by devices" series={this.state.barSeries} categories={this.state.barCategories} />
|
||||
</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Paper className={classes.paper}>
|
||||
<LineChart label="# of messages per second" series={this.state.lineSeries} />
|
||||
</Paper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(Dashboard);
|
@ -0,0 +1,92 @@
|
||||
import React, { Component } from 'react';
|
||||
import Chart from 'react-apexcharts';
|
||||
import { blueGrey, green, red, orange, amber } from '@material-ui/core/colors';
|
||||
|
||||
export class BarChart extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
options: {},
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.categories !== this.props.categories) {
|
||||
this.setState({options: {
|
||||
chart: {
|
||||
stacked: true,
|
||||
animations: {
|
||||
enabled: true,
|
||||
easing: 'linear',
|
||||
speed: 250,
|
||||
animateGradually: {
|
||||
enabled: false,
|
||||
},
|
||||
dynamicAnimation: {
|
||||
enabled: true,
|
||||
speed: 250
|
||||
}
|
||||
},
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
horizontal: true,
|
||||
},
|
||||
},
|
||||
colors: [blueGrey[500], blueGrey[700], blueGrey[900]],
|
||||
stroke: {
|
||||
width: 1,
|
||||
colors: ['#fff']
|
||||
},
|
||||
title: {
|
||||
text: this.props.label,
|
||||
style: {
|
||||
fontSize: '22px',
|
||||
fontWeight: 600,
|
||||
fontFamily: 'Helvetica, Arial, sans-serif',
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
categories: this.props.categories,
|
||||
labels: {
|
||||
formatter: function (val) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
},
|
||||
yaxis: {
|
||||
title: {
|
||||
text: undefined
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
y: {
|
||||
formatter: function (val) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
},
|
||||
fill: {
|
||||
opacity: 1
|
||||
},
|
||||
legend: {
|
||||
position: 'top',
|
||||
}
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Chart
|
||||
options={this.state.options}
|
||||
series={this.props.series}
|
||||
type="bar"
|
||||
height={600}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default BarChart;
|
@ -0,0 +1,67 @@
|
||||
import React, { Component } from 'react';
|
||||
import Chart from 'react-apexcharts';
|
||||
import { blueGrey, green, red } from '@material-ui/core/colors';
|
||||
|
||||
|
||||
export class DonutChart extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
options: {
|
||||
legend: {
|
||||
fontSize: '18px',
|
||||
},
|
||||
plotOptions: {
|
||||
pie: {
|
||||
startAngle: 0,
|
||||
expandOnClick: false,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
customScale: 1,
|
||||
dataLabels: {
|
||||
offset: 0,
|
||||
minAngleToShowLabel: 10
|
||||
},
|
||||
donut: {
|
||||
size: '65%',
|
||||
background: 'transparent',
|
||||
labels: {
|
||||
show: true,
|
||||
total: {
|
||||
show: true,
|
||||
showAlways: true,
|
||||
label: props.totalLabel,
|
||||
fontSize: '22px',
|
||||
fontFamily: 'Helvetica, Arial, sans-serif',
|
||||
fontWeight: 600,
|
||||
color: '#373d3f',
|
||||
formatter: function (w) {
|
||||
return w.globals.seriesTotals.reduce((a, b) => {
|
||||
return a + b
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
colors: [green[500], blueGrey[500], red[500]],
|
||||
labels: ['Online', 'Offline', 'Error / Unknown']},
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Chart
|
||||
options={this.state.options}
|
||||
series={this.props.series}
|
||||
type="donut"/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default DonutChart;
|
@ -0,0 +1,55 @@
|
||||
import React, { Component } from 'react';
|
||||
import Chart from 'react-apexcharts';
|
||||
import { blueGrey, green, red } from '@material-ui/core/colors';
|
||||
|
||||
export class HeatmapChart extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
options: {
|
||||
chart: {
|
||||
animations: {
|
||||
enabled: true,
|
||||
easing: 'linear',
|
||||
speed: 250,
|
||||
animateGradually: {
|
||||
enabled: false,
|
||||
speed: 250,
|
||||
},
|
||||
dynamicAnimation: {
|
||||
enabled: true,
|
||||
speed: 250
|
||||
}
|
||||
}
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
colors: [blueGrey[900]],
|
||||
title: {
|
||||
text: props.label,
|
||||
style: {
|
||||
fontSize: '22px',
|
||||
fontWeight: 600,
|
||||
fontFamily: 'Helvetica, Arial, sans-serif',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Chart
|
||||
options={this.state.options}
|
||||
series={this.props.series}
|
||||
type="heatmap"
|
||||
height={600}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default HeatmapChart
|
@ -0,0 +1,74 @@
|
||||
import React, { Component } from 'react';
|
||||
import Chart from 'react-apexcharts';
|
||||
import { blueGrey, green, red } from '@material-ui/core/colors';
|
||||
|
||||
export class LineChart extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
options: {
|
||||
chart: {
|
||||
animations: {
|
||||
enabled: true,
|
||||
easing: 'linear',
|
||||
speed: 250,
|
||||
animateGradually: {
|
||||
enabled: false,
|
||||
},
|
||||
dynamicAnimation: {
|
||||
enabled: true,
|
||||
speed: 250
|
||||
}
|
||||
},
|
||||
zoom: {
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
colors: [blueGrey[900]],
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
stroke: {
|
||||
curve: 'straight'
|
||||
},
|
||||
title: {
|
||||
text: this.props.label,
|
||||
align: 'left',
|
||||
style: {
|
||||
fontSize: '22px',
|
||||
fontWeight: 600,
|
||||
fontFamily: 'Helvetica, Arial, sans-serif',
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
row: {
|
||||
colors: ['#f3f3f3', 'transparent'], // takes an array which will be repeated on columns
|
||||
opacity: 0.5
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
labels: {
|
||||
formatter: function (val) {
|
||||
return new Date(val).toLocaleTimeString('hu-HU');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Chart
|
||||
options={this.state.options}
|
||||
series={this.props.series}
|
||||
type="line"
|
||||
height={600}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default LineChart
|
@ -0,0 +1,61 @@
|
||||
import { TextField } from '@material-ui/core';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
import DialogContent from '@material-ui/core/DialogContent';
|
||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export default function AddNewDialog(props) {
|
||||
const [name, setName] = useState("");
|
||||
const [url, setUrl] = useState("");
|
||||
|
||||
const onNameChange = (event) => {
|
||||
setName(event.target.value);
|
||||
}
|
||||
|
||||
const onUrlChange = (event) => {
|
||||
setUrl(event.target.value);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Dialog
|
||||
open={props.open}
|
||||
onClose={props.handleClose}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">{"Add new service."}</DialogTitle>
|
||||
<DialogContent>
|
||||
<TextField
|
||||
autoFocus
|
||||
margin="dense"
|
||||
id="name"
|
||||
label="Name"
|
||||
type="text"
|
||||
onChange={onNameChange}
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
autoFocus
|
||||
margin="dense"
|
||||
id="url"
|
||||
label="Url"
|
||||
type="text"
|
||||
onChange={onUrlChange}
|
||||
fullWidth
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={props.handleClose} color="primary">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={() => props.handleAdd(name, url)} color="primary" autoFocus>
|
||||
Add
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
import DialogContent from '@material-ui/core/DialogContent';
|
||||
import DialogContentText from '@material-ui/core/DialogContentText';
|
||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||
import React from 'react';
|
||||
|
||||
export default function DeleteDialog(props) {
|
||||
return (
|
||||
<div>
|
||||
<Dialog
|
||||
open={props.open}
|
||||
onClose={() => props.handleClose(false)}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">{"Are you sure?"}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
Deleting is permament.
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => props.handleClose(false)} color="primary">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={() => props.handleClose(true)} color="primary" autoFocus>
|
||||
OK
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,234 @@
|
||||
import { Box, FormControlLabel, Grid, IconButton, Paper, TextField, Typography } from '@material-ui/core';
|
||||
import Accordion from '@material-ui/core/Accordion';
|
||||
import AccordionDetails from '@material-ui/core/AccordionDetails';
|
||||
import AccordionSummary from '@material-ui/core/AccordionSummary';
|
||||
import { blueGrey, green, red } from '@material-ui/core/colors';
|
||||
import { CancelRounded, CheckCircleRounded, Delete, Edit } from '@material-ui/icons/';
|
||||
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
||||
import { withStyles } from '@material-ui/styles';
|
||||
import React, { Component } from 'react';
|
||||
import DeleteDialog from './DeleteDialog';
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
flexGrow: 1,
|
||||
padding: '64px',
|
||||
backgroundColor: theme.palette.primary.dark,
|
||||
},
|
||||
acc_summary: {
|
||||
backgroundColor: blueGrey[50],
|
||||
padding: theme.spacing(2),
|
||||
textAlign: 'center',
|
||||
height: '75px',
|
||||
},
|
||||
acc_details: {
|
||||
backgroundColor: blueGrey[100],
|
||||
},
|
||||
grid_typo: {
|
||||
fontSize: theme.typography.pxToRem(20),
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
},
|
||||
grid_typo_2: {
|
||||
marginLeft: '5px',
|
||||
fontSize: theme.typography.pxToRem(15),
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
typo: {
|
||||
fontSize: theme.typography.pxToRem(20),
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
},
|
||||
icon_box: {
|
||||
marginLeft: '30px',
|
||||
},
|
||||
paper: {
|
||||
backgroundColor: blueGrey[50],
|
||||
padding: theme.spacing(2),
|
||||
textAlign: 'center',
|
||||
height: '75px',
|
||||
}
|
||||
});
|
||||
|
||||
class ServiceInfoComponent extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isDialogOpen: false,
|
||||
isEditing: false,
|
||||
name: "",
|
||||
url: "",
|
||||
}
|
||||
|
||||
this.handleDialogClose = this.handleDialogClose.bind(this);
|
||||
this.onNameChange = this.onNameChange.bind(this);
|
||||
this.onUrlChange = this.onUrlChange.bind(this);
|
||||
this.handleSaveCancel = this.handleSaveCancel.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({ name: this.props.info.service.name, url: this.props.info.service.uri });
|
||||
|
||||
if (this.props.isEditing !== undefined) {
|
||||
this.setState({ isEditing: this.props.isEditing });
|
||||
}
|
||||
}
|
||||
|
||||
onNameChange(event) {
|
||||
this.setState({ name: event.target.value });
|
||||
}
|
||||
|
||||
onUrlChange(event) {
|
||||
this.setState({ url: event.target.value });
|
||||
}
|
||||
|
||||
getColor(status) {
|
||||
if (status === "OK")
|
||||
return { color: green[600] };
|
||||
else
|
||||
return { color: red[600] };
|
||||
}
|
||||
|
||||
handleDialogClose(result) {
|
||||
this.setState({ isDialogOpen: false });
|
||||
if (result === true) {
|
||||
this.props.service.delete(this.props.info.service.id);
|
||||
}
|
||||
}
|
||||
|
||||
handleEditCancel(value) {
|
||||
this.setState({ isEditing: value });
|
||||
}
|
||||
|
||||
handleSaveCancel() {
|
||||
let request = {
|
||||
...this.props.info.service
|
||||
};
|
||||
|
||||
request.name = this.state.name;
|
||||
request.uri = this.state.url;
|
||||
|
||||
if (request.id > 0) {
|
||||
this.props.service.put(request).catch(ex => {
|
||||
console.log(ex);
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.props.service.post(request).catch(ex => {
|
||||
console.log(ex);
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({ isEditing: false });
|
||||
}
|
||||
|
||||
renderSaveCancel() {
|
||||
return (
|
||||
<Box styles={{ marginLeft: 'auto' }}>
|
||||
<IconButton color="primary" onClick={this.handleSaveCancel}>
|
||||
<CheckCircleRounded fontSize="large" />
|
||||
</IconButton>
|
||||
<IconButton color="primary" onClick={() => this.setState({ isEditing: false })}>
|
||||
<CancelRounded fontSize="large" />
|
||||
</IconButton>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
renderButtons() {
|
||||
const renderEditDelete = () => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<DeleteDialog open={this.state.isDialogOpen} handleClose={this.handleDialogClose}/>
|
||||
<IconButton color="primary" onClick={() => this.handleEditCancel(true)}>
|
||||
<Edit fontSize="large"/>
|
||||
</IconButton>
|
||||
<IconButton style={{color: red[600]}} onClick={() => this.setState({ isDialogOpen: true })}>
|
||||
<Delete fontSize="large"/>
|
||||
</IconButton>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
const { classes } = this.props;
|
||||
return (
|
||||
<Box className={classes.icon_box}>
|
||||
{this.props.isAdmin && this.props.info.service.name !== "Mqtt Client Service" ? renderEditDelete() : null}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
|
||||
const renderAccordion = () => {
|
||||
return (
|
||||
<Accordion>
|
||||
<AccordionSummary className={classes.acc_summary}
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
aria-controls={"device-panel-/" + this.props.info.service.name}
|
||||
id={"device-panel-/" + this.props.info.service.name}>
|
||||
<Grid container
|
||||
spacing={1}
|
||||
direction="row"
|
||||
justify="flex-start"
|
||||
alignItems="center">
|
||||
<Grid item>
|
||||
<Typography className={classes.grid_typo}>{this.props.info.service.name}</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography className={classes.grid_typo_2}>{this.props.info.service.uri}</Typography>
|
||||
</Grid>
|
||||
<Grid item style={{ marginLeft: 'auto' }}>
|
||||
<Grid container
|
||||
spacing={1}
|
||||
direction="row"
|
||||
justify="flex-start"
|
||||
alignItems="center">
|
||||
<Grid item>
|
||||
<FormControlLabel
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
onFocus={(event) => event.stopPropagation()}
|
||||
control={this.renderButtons()} />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography style={this.getColor(this.props.info.statusCode)}>Status: <b>{this.props.info.statusCode}</b></Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails className={classes.acc_details}>
|
||||
{this.props.info.response}
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
const renderTextFields = () => {
|
||||
return (
|
||||
<Paper className={classes.acc_summary}>
|
||||
<Grid container
|
||||
spacing={1}
|
||||
direction="row"
|
||||
justify="flex-start"
|
||||
alignItems="center">
|
||||
<Grid item xs>
|
||||
<TextField label="Name" type="text" defaultValue={this.props.info.service.name} onChange={this.onNameChange} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<TextField label="Url" type="text" fullWidth defaultValue={this.props.info.service.uri} onChange={this.onUrlChange}/>
|
||||
</Grid>
|
||||
<Grid item xs>
|
||||
{this.renderSaveCancel()}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
||||
return this.state.isEditing ? renderTextFields() : renderAccordion();
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(ServiceInfoComponent);
|
@ -0,0 +1,386 @@
|
||||
"use strict";
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
//----------------------
|
||||
// <auto-generated>
|
||||
// Generated using the NSwag toolchain v13.8.2.0 (NJsonSchema v10.2.1.0 (Newtonsoft.Json v12.0.0.0)) (http://NSwag.org)
|
||||
// </auto-generated>
|
||||
//----------------------
|
||||
// ReSharper disable InconsistentNaming
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = function (d, b) {
|
||||
extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
||||
return extendStatics(d, b);
|
||||
};
|
||||
return function (d, b) {
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ApiException = exports.HttpStatusCode = exports.ServiceRequest = exports.ServiceInfo = void 0;
|
||||
var ServiceInfoService = /** @class */ (function () {
|
||||
function ServiceInfoService(baseUrl, http) {
|
||||
this.jsonParseReviver = undefined;
|
||||
this.http = http ? http : window;
|
||||
this.baseUrl = baseUrl !== undefined && baseUrl !== null ? baseUrl : "";
|
||||
}
|
||||
ServiceInfoService.prototype.getCount = function () {
|
||||
var _this = this;
|
||||
var url_ = this.baseUrl + "/api/Services/count";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
var options_ = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
return this.http.fetch(url_, options_).then(function (_response) {
|
||||
return _this.processGetCount(_response);
|
||||
});
|
||||
};
|
||||
ServiceInfoService.prototype.processGetCount = function (response) {
|
||||
var _this = this;
|
||||
var status = response.status;
|
||||
var _headers = {};
|
||||
if (response.headers && response.headers.forEach) {
|
||||
response.headers.forEach(function (v, k) { return _headers[k] = v; });
|
||||
}
|
||||
;
|
||||
if (status === 200) {
|
||||
return response.text().then(function (_responseText) {
|
||||
var result200 = null;
|
||||
var resultData200 = _responseText === "" ? null : JSON.parse(_responseText, _this.jsonParseReviver);
|
||||
result200 = resultData200 !== undefined ? resultData200 : null;
|
||||
return result200;
|
||||
});
|
||||
}
|
||||
else if (status !== 200 && status !== 204) {
|
||||
return response.text().then(function (_responseText) {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
};
|
||||
ServiceInfoService.prototype.get = function () {
|
||||
var _this = this;
|
||||
var url_ = this.baseUrl + "/api/Services";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
var options_ = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
return this.http.fetch(url_, options_).then(function (_response) {
|
||||
return _this.processGet(_response);
|
||||
});
|
||||
};
|
||||
ServiceInfoService.prototype.processGet = function (response) {
|
||||
var _this = this;
|
||||
var status = response.status;
|
||||
var _headers = {};
|
||||
if (response.headers && response.headers.forEach) {
|
||||
response.headers.forEach(function (v, k) { return _headers[k] = v; });
|
||||
}
|
||||
;
|
||||
if (status === 200) {
|
||||
return response.text().then(function (_responseText) {
|
||||
var result200 = null;
|
||||
var resultData200 = _responseText === "" ? null : JSON.parse(_responseText, _this.jsonParseReviver);
|
||||
if (Array.isArray(resultData200)) {
|
||||
result200 = [];
|
||||
for (var _i = 0, resultData200_1 = resultData200; _i < resultData200_1.length; _i++) {
|
||||
var item = resultData200_1[_i];
|
||||
result200.push(ServiceInfo.fromJS(item));
|
||||
}
|
||||
}
|
||||
return result200;
|
||||
});
|
||||
}
|
||||
else if (status !== 200 && status !== 204) {
|
||||
return response.text().then(function (_responseText) {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
};
|
||||
ServiceInfoService.prototype.post = function (request) {
|
||||
var _this = this;
|
||||
var url_ = this.baseUrl + "/api/Services";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
var content_ = JSON.stringify(request);
|
||||
var options_ = {
|
||||
body: content_,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
return this.http.fetch(url_, options_).then(function (_response) {
|
||||
return _this.processPost(_response);
|
||||
});
|
||||
};
|
||||
ServiceInfoService.prototype.processPost = function (response) {
|
||||
var _this = this;
|
||||
var status = response.status;
|
||||
var _headers = {};
|
||||
if (response.headers && response.headers.forEach) {
|
||||
response.headers.forEach(function (v, k) { return _headers[k] = v; });
|
||||
}
|
||||
;
|
||||
if (status === 201) {
|
||||
return response.text().then(function (_responseText) {
|
||||
var result201 = null;
|
||||
var resultData201 = _responseText === "" ? null : JSON.parse(_responseText, _this.jsonParseReviver);
|
||||
result201 = ServiceRequest.fromJS(resultData201);
|
||||
return result201;
|
||||
});
|
||||
}
|
||||
else if (status !== 200 && status !== 204) {
|
||||
return response.text().then(function (_responseText) {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
};
|
||||
ServiceInfoService.prototype.put = function (request) {
|
||||
var _this = this;
|
||||
var url_ = this.baseUrl + "/api/Services";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
var content_ = JSON.stringify(request);
|
||||
var options_ = {
|
||||
body: content_,
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
return this.http.fetch(url_, options_).then(function (_response) {
|
||||
return _this.processPut(_response);
|
||||
});
|
||||
};
|
||||
ServiceInfoService.prototype.processPut = function (response) {
|
||||
var status = response.status;
|
||||
var _headers = {};
|
||||
if (response.headers && response.headers.forEach) {
|
||||
response.headers.forEach(function (v, k) { return _headers[k] = v; });
|
||||
}
|
||||
;
|
||||
if (status === 204) {
|
||||
return response.text().then(function (_responseText) {
|
||||
return;
|
||||
});
|
||||
}
|
||||
else if (status !== 200 && status !== 204) {
|
||||
return response.text().then(function (_responseText) {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
};
|
||||
ServiceInfoService.prototype.delete = function (id) {
|
||||
var _this = this;
|
||||
var url_ = this.baseUrl + "/api/Services/{id}";
|
||||
if (id === undefined || id === null)
|
||||
throw new Error("The parameter 'id' must be defined.");
|
||||
url_ = url_.replace("{id}", encodeURIComponent("" + id));
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
var options_ = {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
return this.http.fetch(url_, options_).then(function (_response) {
|
||||
return _this.processDelete(_response);
|
||||
});
|
||||
};
|
||||
ServiceInfoService.prototype.processDelete = function (response) {
|
||||
var status = response.status;
|
||||
var _headers = {};
|
||||
if (response.headers && response.headers.forEach) {
|
||||
response.headers.forEach(function (v, k) { return _headers[k] = v; });
|
||||
}
|
||||
;
|
||||
if (status === 204) {
|
||||
return response.text().then(function (_responseText) {
|
||||
return;
|
||||
});
|
||||
}
|
||||
else if (status !== 200 && status !== 204) {
|
||||
return response.text().then(function (_responseText) {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
};
|
||||
return ServiceInfoService;
|
||||
}());
|
||||
exports.default = ServiceInfoService;
|
||||
var ServiceInfo = /** @class */ (function () {
|
||||
function ServiceInfo(data) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
this[property] = data[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
ServiceInfo.prototype.init = function (_data) {
|
||||
if (_data) {
|
||||
this.service = _data["service"] ? ServiceRequest.fromJS(_data["service"]) : undefined;
|
||||
this.statusCode = _data["statusCode"];
|
||||
this.response = _data["response"];
|
||||
}
|
||||
};
|
||||
ServiceInfo.fromJS = function (data) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
var result = new ServiceInfo();
|
||||
result.init(data);
|
||||
return result;
|
||||
};
|
||||
ServiceInfo.prototype.toJSON = function (data) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
data["service"] = this.service ? this.service.toJSON() : undefined;
|
||||
data["statusCode"] = this.statusCode;
|
||||
data["response"] = this.response;
|
||||
return data;
|
||||
};
|
||||
return ServiceInfo;
|
||||
}());
|
||||
exports.ServiceInfo = ServiceInfo;
|
||||
var ServiceRequest = /** @class */ (function () {
|
||||
function ServiceRequest(data) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
this[property] = data[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
ServiceRequest.prototype.init = function (_data) {
|
||||
if (_data) {
|
||||
this.id = _data["id"];
|
||||
this.name = _data["name"];
|
||||
this.uri = _data["uri"];
|
||||
}
|
||||
};
|
||||
ServiceRequest.fromJS = function (data) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
var result = new ServiceRequest();
|
||||
result.init(data);
|
||||
return result;
|
||||
};
|
||||
ServiceRequest.prototype.toJSON = function (data) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
data["id"] = this.id;
|
||||
data["name"] = this.name;
|
||||
data["uri"] = this.uri;
|
||||
return data;
|
||||
};
|
||||
return ServiceRequest;
|
||||
}());
|
||||
exports.ServiceRequest = ServiceRequest;
|
||||
var HttpStatusCode;
|
||||
(function (HttpStatusCode) {
|
||||
HttpStatusCode["Continue"] = "Continue";
|
||||
HttpStatusCode["SwitchingProtocols"] = "SwitchingProtocols";
|
||||
HttpStatusCode["Processing"] = "Processing";
|
||||
HttpStatusCode["EarlyHints"] = "EarlyHints";
|
||||
HttpStatusCode["OK"] = "OK";
|
||||
HttpStatusCode["Created"] = "Created";
|
||||
HttpStatusCode["Accepted"] = "Accepted";
|
||||
HttpStatusCode["NonAuthoritativeInformation"] = "NonAuthoritativeInformation";
|
||||
HttpStatusCode["NoContent"] = "NoContent";
|
||||
HttpStatusCode["ResetContent"] = "ResetContent";
|
||||
HttpStatusCode["PartialContent"] = "PartialContent";
|
||||
HttpStatusCode["MultiStatus"] = "MultiStatus";
|
||||
HttpStatusCode["AlreadyReported"] = "AlreadyReported";
|
||||
HttpStatusCode["IMUsed"] = "IMUsed";
|
||||
HttpStatusCode["MultipleChoices"] = "Ambiguous";
|
||||
HttpStatusCode["Ambiguous"] = "Ambiguous";
|
||||
HttpStatusCode["MovedPermanently"] = "Moved";
|
||||
HttpStatusCode["Moved"] = "Moved";
|
||||
HttpStatusCode["Found"] = "Redirect";
|
||||
HttpStatusCode["Redirect"] = "Redirect";
|
||||
HttpStatusCode["SeeOther"] = "RedirectMethod";
|
||||
HttpStatusCode["RedirectMethod"] = "RedirectMethod";
|
||||
HttpStatusCode["NotModified"] = "NotModified";
|
||||
HttpStatusCode["UseProxy"] = "UseProxy";
|
||||
HttpStatusCode["Unused"] = "Unused";
|
||||
HttpStatusCode["TemporaryRedirect"] = "TemporaryRedirect";
|
||||
HttpStatusCode["RedirectKeepVerb"] = "TemporaryRedirect";
|
||||
HttpStatusCode["PermanentRedirect"] = "PermanentRedirect";
|
||||
HttpStatusCode["BadRequest"] = "BadRequest";
|
||||
HttpStatusCode["Unauthorized"] = "Unauthorized";
|
||||
HttpStatusCode["PaymentRequired"] = "PaymentRequired";
|
||||
HttpStatusCode["Forbidden"] = "Forbidden";
|
||||
HttpStatusCode["NotFound"] = "NotFound";
|
||||
HttpStatusCode["MethodNotAllowed"] = "MethodNotAllowed";
|
||||
HttpStatusCode["NotAcceptable"] = "NotAcceptable";
|
||||
HttpStatusCode["ProxyAuthenticationRequired"] = "ProxyAuthenticationRequired";
|
||||
HttpStatusCode["RequestTimeout"] = "RequestTimeout";
|
||||
HttpStatusCode["Conflict"] = "Conflict";
|
||||
HttpStatusCode["Gone"] = "Gone";
|
||||
HttpStatusCode["LengthRequired"] = "LengthRequired";
|
||||
HttpStatusCode["PreconditionFailed"] = "PreconditionFailed";
|
||||
HttpStatusCode["RequestEntityTooLarge"] = "RequestEntityTooLarge";
|
||||
HttpStatusCode["RequestUriTooLong"] = "RequestUriTooLong";
|
||||
HttpStatusCode["UnsupportedMediaType"] = "UnsupportedMediaType";
|
||||
HttpStatusCode["RequestedRangeNotSatisfiable"] = "RequestedRangeNotSatisfiable";
|
||||
HttpStatusCode["ExpectationFailed"] = "ExpectationFailed";
|
||||
HttpStatusCode["MisdirectedRequest"] = "MisdirectedRequest";
|
||||
HttpStatusCode["UnprocessableEntity"] = "UnprocessableEntity";
|
||||
HttpStatusCode["Locked"] = "Locked";
|
||||
HttpStatusCode["FailedDependency"] = "FailedDependency";
|
||||
HttpStatusCode["UpgradeRequired"] = "UpgradeRequired";
|
||||
HttpStatusCode["PreconditionRequired"] = "PreconditionRequired";
|
||||
HttpStatusCode["TooManyRequests"] = "TooManyRequests";
|
||||
HttpStatusCode["RequestHeaderFieldsTooLarge"] = "RequestHeaderFieldsTooLarge";
|
||||
HttpStatusCode["UnavailableForLegalReasons"] = "UnavailableForLegalReasons";
|
||||
HttpStatusCode["InternalServerError"] = "InternalServerError";
|
||||
HttpStatusCode["NotImplemented"] = "NotImplemented";
|
||||
HttpStatusCode["BadGateway"] = "BadGateway";
|
||||
HttpStatusCode["ServiceUnavailable"] = "ServiceUnavailable";
|
||||
HttpStatusCode["GatewayTimeout"] = "GatewayTimeout";
|
||||
HttpStatusCode["HttpVersionNotSupported"] = "HttpVersionNotSupported";
|
||||
HttpStatusCode["VariantAlsoNegotiates"] = "VariantAlsoNegotiates";
|
||||
HttpStatusCode["InsufficientStorage"] = "InsufficientStorage";
|
||||
HttpStatusCode["LoopDetected"] = "LoopDetected";
|
||||
HttpStatusCode["NotExtended"] = "NotExtended";
|
||||
HttpStatusCode["NetworkAuthenticationRequired"] = "NetworkAuthenticationRequired";
|
||||
})(HttpStatusCode = exports.HttpStatusCode || (exports.HttpStatusCode = {}));
|
||||
var ApiException = /** @class */ (function (_super) {
|
||||
__extends(ApiException, _super);
|
||||
function ApiException(message, status, response, headers, result) {
|
||||
var _this = _super.call(this) || this;
|
||||
_this.isApiException = true;
|
||||
_this.message = message;
|
||||
_this.status = status;
|
||||
_this.response = response;
|
||||
_this.headers = headers;
|
||||
_this.result = result;
|
||||
return _this;
|
||||
}
|
||||
ApiException.isApiException = function (obj) {
|
||||
return obj.isApiException === true;
|
||||
};
|
||||
return ApiException;
|
||||
}(Error));
|
||||
exports.ApiException = ApiException;
|
||||
function throwException(message, status, response, headers, result) {
|
||||
if (result !== null && result !== undefined)
|
||||
throw result;
|
||||
else
|
||||
throw new ApiException(message, status, response, headers, null);
|
||||
}
|
||||
//# sourceMappingURL=SystemInfoService.js.map
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,389 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
//----------------------
|
||||
// <auto-generated>
|
||||
// Generated using the NSwag toolchain v13.8.2.0 (NJsonSchema v10.2.1.0 (Newtonsoft.Json v12.0.0.0)) (http://NSwag.org)
|
||||
// </auto-generated>
|
||||
//----------------------
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
export default class ServiceInfoService {
|
||||
private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
|
||||
private baseUrl: string;
|
||||
protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
|
||||
|
||||
constructor(baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> }) {
|
||||
this.http = http ? http : <any>window;
|
||||
this.baseUrl = baseUrl !== undefined && baseUrl !== null ? baseUrl : "";
|
||||
}
|
||||
|
||||
getCount(): Promise<number> {
|
||||
let url_ = this.baseUrl + "/api/Services/count";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_ = <RequestInit>{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processGetCount(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processGetCount(response: Response): Promise<number> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 200) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result200: any = null;
|
||||
let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
result200 = resultData200 !== undefined ? resultData200 : <any>null;
|
||||
return result200;
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<number>(<any>null);
|
||||
}
|
||||
|
||||
get(): Promise<ServiceInfo[]> {
|
||||
let url_ = this.baseUrl + "/api/Services";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_ = <RequestInit>{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processGet(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processGet(response: Response): Promise<ServiceInfo[]> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 200) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result200: any = null;
|
||||
let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
if (Array.isArray(resultData200)) {
|
||||
result200 = [] as any;
|
||||
for (let item of resultData200)
|
||||
result200!.push(ServiceInfo.fromJS(item));
|
||||
}
|
||||
return result200;
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<ServiceInfo[]>(<any>null);
|
||||
}
|
||||
|
||||
post(request: ServiceRequest): Promise<ServiceRequest> {
|
||||
let url_ = this.baseUrl + "/api/Services";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
const content_ = JSON.stringify(request);
|
||||
|
||||
let options_ = <RequestInit>{
|
||||
body: content_,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processPost(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processPost(response: Response): Promise<ServiceRequest> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 201) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result201: any = null;
|
||||
let resultData201 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
result201 = ServiceRequest.fromJS(resultData201);
|
||||
return result201;
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<ServiceRequest>(<any>null);
|
||||
}
|
||||
|
||||
put(request: ServiceRequest): Promise<void> {
|
||||
let url_ = this.baseUrl + "/api/Services";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
const content_ = JSON.stringify(request);
|
||||
|
||||
let options_ = <RequestInit>{
|
||||
body: content_,
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processPut(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processPut(response: Response): Promise<void> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return;
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<void>(<any>null);
|
||||
}
|
||||
|
||||
delete(id: number): Promise<void> {
|
||||
let url_ = this.baseUrl + "/api/Services/{id}";
|
||||
if (id === undefined || id === null)
|
||||
throw new Error("The parameter 'id' must be defined.");
|
||||
url_ = url_.replace("{id}", encodeURIComponent("" + id));
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_ = <RequestInit>{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processDelete(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processDelete(response: Response): Promise<void> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return;
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<void>(<any>null);
|
||||
}
|
||||
}
|
||||
|
||||
export class ServiceInfo implements IServiceInfo {
|
||||
service?: ServiceRequest | undefined;
|
||||
statusCode!: HttpStatusCode;
|
||||
response?: string | undefined;
|
||||
|
||||
constructor(data?: IServiceInfo) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
(<any>this)[property] = (<any>data)[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(_data?: any) {
|
||||
if (_data) {
|
||||
this.service = _data["service"] ? ServiceRequest.fromJS(_data["service"]) : <any>undefined;
|
||||
this.statusCode = _data["statusCode"];
|
||||
this.response = _data["response"];
|
||||
}
|
||||
}
|
||||
|
||||
static fromJS(data: any): ServiceInfo {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
let result = new ServiceInfo();
|
||||
result.init(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON(data?: any) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
data["service"] = this.service ? this.service.toJSON() : <any>undefined;
|
||||
data["statusCode"] = this.statusCode;
|
||||
data["response"] = this.response;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IServiceInfo {
|
||||
service?: ServiceRequest | undefined;
|
||||
statusCode: HttpStatusCode;
|
||||
response?: string | undefined;
|
||||
}
|
||||
|
||||
export class ServiceRequest implements IServiceRequest {
|
||||
id!: number;
|
||||
name?: string | undefined;
|
||||
uri?: string | undefined;
|
||||
|
||||
constructor(data?: IServiceRequest) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
(<any>this)[property] = (<any>data)[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(_data?: any) {
|
||||
if (_data) {
|
||||
this.id = _data["id"];
|
||||
this.name = _data["name"];
|
||||
this.uri = _data["uri"];
|
||||
}
|
||||
}
|
||||
|
||||
static fromJS(data: any): ServiceRequest {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
let result = new ServiceRequest();
|
||||
result.init(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON(data?: any) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
data["id"] = this.id;
|
||||
data["name"] = this.name;
|
||||
data["uri"] = this.uri;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IServiceRequest {
|
||||
id: number;
|
||||
name?: string | undefined;
|
||||
uri?: string | undefined;
|
||||
}
|
||||
|
||||
export enum HttpStatusCode {
|
||||
Continue = "Continue",
|
||||
SwitchingProtocols = "SwitchingProtocols",
|
||||
Processing = "Processing",
|
||||
EarlyHints = "EarlyHints",
|
||||
OK = "OK",
|
||||
Created = "Created",
|
||||
Accepted = "Accepted",
|
||||
NonAuthoritativeInformation = "NonAuthoritativeInformation",
|
||||
NoContent = "NoContent",
|
||||
ResetContent = "ResetContent",
|
||||
PartialContent = "PartialContent",
|
||||
MultiStatus = "MultiStatus",
|
||||
AlreadyReported = "AlreadyReported",
|
||||
IMUsed = "IMUsed",
|
||||
MultipleChoices = "Ambiguous",
|
||||
Ambiguous = "Ambiguous",
|
||||
MovedPermanently = "Moved",
|
||||
Moved = "Moved",
|
||||
Found = "Redirect",
|
||||
Redirect = "Redirect",
|
||||
SeeOther = "RedirectMethod",
|
||||
RedirectMethod = "RedirectMethod",
|
||||
NotModified = "NotModified",
|
||||
UseProxy = "UseProxy",
|
||||
Unused = "Unused",
|
||||
TemporaryRedirect = "TemporaryRedirect",
|
||||
RedirectKeepVerb = "TemporaryRedirect",
|
||||
PermanentRedirect = "PermanentRedirect",
|
||||
BadRequest = "BadRequest",
|
||||
Unauthorized = "Unauthorized",
|
||||
PaymentRequired = "PaymentRequired",
|
||||
Forbidden = "Forbidden",
|
||||
NotFound = "NotFound",
|
||||
MethodNotAllowed = "MethodNotAllowed",
|
||||
NotAcceptable = "NotAcceptable",
|
||||
ProxyAuthenticationRequired = "ProxyAuthenticationRequired",
|
||||
RequestTimeout = "RequestTimeout",
|
||||
Conflict = "Conflict",
|
||||
Gone = "Gone",
|
||||
LengthRequired = "LengthRequired",
|
||||
PreconditionFailed = "PreconditionFailed",
|
||||
RequestEntityTooLarge = "RequestEntityTooLarge",
|
||||
RequestUriTooLong = "RequestUriTooLong",
|
||||
UnsupportedMediaType = "UnsupportedMediaType",
|
||||
RequestedRangeNotSatisfiable = "RequestedRangeNotSatisfiable",
|
||||
ExpectationFailed = "ExpectationFailed",
|
||||
MisdirectedRequest = "MisdirectedRequest",
|
||||
UnprocessableEntity = "UnprocessableEntity",
|
||||
Locked = "Locked",
|
||||
FailedDependency = "FailedDependency",
|
||||
UpgradeRequired = "UpgradeRequired",
|
||||
PreconditionRequired = "PreconditionRequired",
|
||||
TooManyRequests = "TooManyRequests",
|
||||
RequestHeaderFieldsTooLarge = "RequestHeaderFieldsTooLarge",
|
||||
UnavailableForLegalReasons = "UnavailableForLegalReasons",
|
||||
InternalServerError = "InternalServerError",
|
||||
NotImplemented = "NotImplemented",
|
||||
BadGateway = "BadGateway",
|
||||
ServiceUnavailable = "ServiceUnavailable",
|
||||
GatewayTimeout = "GatewayTimeout",
|
||||
HttpVersionNotSupported = "HttpVersionNotSupported",
|
||||
VariantAlsoNegotiates = "VariantAlsoNegotiates",
|
||||
InsufficientStorage = "InsufficientStorage",
|
||||
LoopDetected = "LoopDetected",
|
||||
NotExtended = "NotExtended",
|
||||
NetworkAuthenticationRequired = "NetworkAuthenticationRequired",
|
||||
}
|
||||
|
||||
export class ApiException extends Error {
|
||||
message: string;
|
||||
status: number;
|
||||
response: string;
|
||||
headers: { [key: string]: any; };
|
||||
result: any;
|
||||
|
||||
constructor(message: string, status: number, response: string, headers: { [key: string]: any; }, result: any) {
|
||||
super();
|
||||
|
||||
this.message = message;
|
||||
this.status = status;
|
||||
this.response = response;
|
||||
this.headers = headers;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
protected isApiException = true;
|
||||
|
||||
static isApiException(obj: any): obj is ApiException {
|
||||
return obj.isApiException === true;
|
||||
}
|
||||
}
|
||||
|
||||
function throwException(message: string, status: number, response: string, headers: { [key: string]: any; }, result?: any): any {
|
||||
if (result !== null && result !== undefined)
|
||||
throw result;
|
||||
else
|
||||
throw new ApiException(message, status, response, headers, null);
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
import { Grid, Typography } from '@material-ui/core';
|
||||
import Accordion from '@material-ui/core/Accordion';
|
||||
import AccordionDetails from '@material-ui/core/AccordionDetails';
|
||||
import AccordionSummary from '@material-ui/core/AccordionSummary';
|
||||
import { blueGrey } from '@material-ui/core/colors';
|
||||
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
||||
import { Skeleton } from '@material-ui/lab';
|
||||
import { withStyles } from '@material-ui/styles';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
const styles = theme => ({
|
||||
acc_summary: {
|
||||
backgroundColor: blueGrey[50],
|
||||
padding: theme.spacing(2),
|
||||
textAlign: 'center',
|
||||
height: '75px',
|
||||
},
|
||||
acc_details: {
|
||||
backgroundColor: blueGrey[100],
|
||||
},
|
||||
grid_typo: {
|
||||
fontSize: theme.typography.pxToRem(20),
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
},
|
||||
grid_typo_2: {
|
||||
marginLeft: '5px',
|
||||
fontSize: theme.typography.pxToRem(15),
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
});
|
||||
|
||||
class ServiceInfoSkeleton extends Component {
|
||||
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
|
||||
return (
|
||||
<Accordion>
|
||||
<AccordionSummary className={classes.acc_summary}
|
||||
expandIcon={<ExpandMoreIcon />}>
|
||||
<Grid container
|
||||
spacing={1}
|
||||
direction="row"
|
||||
justify="flex-start"
|
||||
alignItems="center">
|
||||
<Grid item>
|
||||
<Typography className={classes.grid_typo}><Skeleton width={200} /></Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography className={classes.grid_typo_2}><Skeleton width={300} /></Typography>
|
||||
</Grid>
|
||||
<Grid item style={{ marginLeft: 'auto' }}>
|
||||
<Grid container
|
||||
spacing={1}
|
||||
direction="row"
|
||||
justify="flex-start"
|
||||
alignItems="center">
|
||||
<Grid item>
|
||||
<Typography><Skeleton width={150} /></Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails className={classes.acc_details}>
|
||||
<Grid container
|
||||
spacing={1}
|
||||
direction="column"
|
||||
justify="flex-start"
|
||||
alignItems="flex-start">
|
||||
<Grid item>
|
||||
<Typography><Skeleton width={800} /></Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography><Skeleton width={300} /></Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography><Skeleton width={500} /></Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(ServiceInfoSkeleton);
|
@ -0,0 +1,146 @@
|
||||
import { Grid, IconButton, Paper, Typography } from '@material-ui/core';
|
||||
import { blueGrey } from '@material-ui/core/colors';
|
||||
import { AddBox, Refresh } from '@material-ui/icons/';
|
||||
import { withStyles } from '@material-ui/styles';
|
||||
import { HubConnectionBuilder } from '@microsoft/signalr';
|
||||
import React, { Component } from 'react';
|
||||
import AddNewDialog from './AddNewDialog';
|
||||
import ServiceInfoService, { ServiceRequest } from './ServiceInfoService';
|
||||
import ServiceInfoComponent from './ServiceInfoComponent';
|
||||
import ServiceInfoSkeleton from './ServiceInfoSkeleton';
|
||||
|
||||
const styles = theme => ({
|
||||
typo: {
|
||||
fontSize: theme.typography.pxToRem(20),
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
},
|
||||
paper: {
|
||||
backgroundColor: blueGrey[50],
|
||||
height: '60px',
|
||||
}
|
||||
});
|
||||
|
||||
const hub_url = "/hubs/services";
|
||||
const notify_method_name = "NotifyUpdatedAsync";
|
||||
|
||||
class Services extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
hubConnection: null,
|
||||
isDialogOpen: false,
|
||||
isLoading: false,
|
||||
service: new ServiceInfoService(),
|
||||
services: [],
|
||||
serviceCount: [1, 2, 3],
|
||||
}
|
||||
|
||||
this.handleDevicesUpdated = this.handleDevicesUpdated.bind(this);
|
||||
this.addDevice = this.addDevice.bind(this);
|
||||
}
|
||||
|
||||
handleDevicesUpdated() {
|
||||
this.setState({ isLoading: true });
|
||||
|
||||
this.state.service.getCount().then(result => {
|
||||
const updatedCount = [];
|
||||
for (var i = 0; i < result; i++) {
|
||||
updatedCount.push(i);
|
||||
}
|
||||
this.setState({ serviceCount: updatedCount });
|
||||
}).catch(ex => {
|
||||
console.log(ex);
|
||||
});
|
||||
|
||||
this.state.service.get().then(result => {
|
||||
const updatedServices = [];
|
||||
for (var s of result) {
|
||||
updatedServices.push(s);
|
||||
}
|
||||
this.setState({ services: updatedServices });
|
||||
}).catch(ex => {
|
||||
console.log(ex);
|
||||
}).finally(() => this.setState({ isLoading: false }));
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.handleDevicesUpdated();
|
||||
const newConnection = new HubConnectionBuilder()
|
||||
.withUrl(hub_url)
|
||||
.withAutomaticReconnect()
|
||||
.build();
|
||||
|
||||
this.setState({ hubConnection: newConnection });
|
||||
|
||||
newConnection.start()
|
||||
.then(_ => {
|
||||
console.log('Services hub Connected!');
|
||||
newConnection.on(notify_method_name, () => this.handleDevicesUpdated());
|
||||
}).catch(e => console.log('Services hub Connection failed: ', e));
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.state.hubConnection != null) {
|
||||
this.state.hubConnection.off(notify_method_name);
|
||||
console.log('Services hub Disconnected!');
|
||||
}
|
||||
}
|
||||
|
||||
addDevice(name, url) {
|
||||
this.setState({ isDialogOpen: false });
|
||||
let request = new ServiceRequest();
|
||||
request.id = 0;
|
||||
request.name = name;
|
||||
request.uri = url;
|
||||
|
||||
this.state.service.post(request).catch(ex => {
|
||||
console.log(ex);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
|
||||
const ServiceComponents = this.state.services.map((info, index) => (
|
||||
<ServiceInfoComponent key={index} isAdmin={this.props.isAdmin} info={info} service={this.state.service} />
|
||||
));
|
||||
|
||||
const Skeletons = this.state.serviceCount.map((i, index) => (
|
||||
<ServiceInfoSkeleton key={index} />
|
||||
));
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Paper className={classes.paper} square>
|
||||
<Grid container
|
||||
spacing={0}
|
||||
direction="row"
|
||||
justify="center"
|
||||
alignItems="center">
|
||||
<Grid item>
|
||||
<Typography className={classes.typo}>Services</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
{this.props.isAdmin ?
|
||||
<IconButton color="primary" onClick={() => this.setState({ isDialogOpen: true })}>
|
||||
<AddBox fontSize="large" />
|
||||
</IconButton>
|
||||
: null
|
||||
}
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<IconButton color="primary" onClick={this.handleDevicesUpdated}>
|
||||
<Refresh fontSize="large" />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
<AddNewDialog open={this.state.isDialogOpen} handleClose={() => this.setState({ isDialogOpen: false })} handleAdd={this.addDevice} />
|
||||
</Grid>
|
||||
</Paper>
|
||||
{this.state.isLoading ? Skeletons : ServiceComponents}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(Services);
|
192
Birdmap.API/ClientApp/src/components/devices/DeviceComponent.jsx
Normal file
192
Birdmap.API/ClientApp/src/components/devices/DeviceComponent.jsx
Normal file
@ -0,0 +1,192 @@
|
||||
import { Box, FormControlLabel, Grid, IconButton, Typography } from '@material-ui/core';
|
||||
import Accordion from '@material-ui/core/Accordion';
|
||||
import AccordionDetails from '@material-ui/core/AccordionDetails';
|
||||
import AccordionSummary from '@material-ui/core/AccordionSummary';
|
||||
import { blueGrey, green, orange, red } from '@material-ui/core/colors';
|
||||
import { Power, PowerOff, Refresh } from '@material-ui/icons/';
|
||||
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
||||
import { withStyles } from '@material-ui/styles';
|
||||
import React, { Component } from 'react';
|
||||
import { withRouter } from "react-router";
|
||||
import DeviceService from '../../common/DeviceService';
|
||||
import DevicesContext from '../../contexts/DevicesContext';
|
||||
|
||||
const styles = theme => ({
|
||||
acc_summary: {
|
||||
backgroundColor: blueGrey[50],
|
||||
},
|
||||
acc_details: {
|
||||
backgroundColor: blueGrey[100],
|
||||
},
|
||||
grid_typo: {
|
||||
fontSize: theme.typography.pxToRem(20),
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
},
|
||||
grid_typo_2: {
|
||||
marginLeft: '5px',
|
||||
fontSize: theme.typography.pxToRem(15),
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
grid_item: {
|
||||
width: '100%',
|
||||
marginLeft: '5px',
|
||||
marginRight: '30px',
|
||||
},
|
||||
grid_item_typo: {
|
||||
fontSize: theme.typography.pxToRem(15),
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
},
|
||||
grid_item_typo_2: {
|
||||
marginLeft: '5px',
|
||||
fontSize: theme.typography.pxToRem(15),
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
icon_box: {
|
||||
marginRight: '15px',
|
||||
}
|
||||
});
|
||||
|
||||
class DeviceComponent extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
expanded: "",
|
||||
}
|
||||
}
|
||||
|
||||
static contextType = DevicesContext;
|
||||
|
||||
getColor(status) {
|
||||
if (status == "Online") {
|
||||
return { color: green[600] };
|
||||
} else if (status == "Offline") {
|
||||
return { color: blueGrey[500] };
|
||||
} else /* if (device.status == "Unknown" || device.status == "Error") */ {
|
||||
return { color: red[800] };
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const id = this.props.match.params.id;
|
||||
this.setState({ expanded: id });
|
||||
}
|
||||
|
||||
renderSensorButtons(device, sensor) {
|
||||
var service = new DeviceService();
|
||||
return this.renderButtons(
|
||||
() => service.onlinesensor(device.id, sensor.id),
|
||||
() => service.offlinesensor(device.id, sensor.id),
|
||||
() => this.context.updateDevice(device.id)
|
||||
);
|
||||
}
|
||||
|
||||
renderDeviceButtons(device) {
|
||||
var service = new DeviceService();
|
||||
return this.renderButtons(
|
||||
() => service.onlinedevice(device.id),
|
||||
() => service.offlinedevice(device.id),
|
||||
() => this.context.updateDevice(device.id)
|
||||
);
|
||||
}
|
||||
|
||||
renderButtons(onPower, onPowerOff, onRefresh) {
|
||||
const renderOnOff = () => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<IconButton color="primary" onClick={onPower}>
|
||||
<Power />
|
||||
</IconButton>
|
||||
<IconButton color="primary" onClick={onPowerOff}>
|
||||
<PowerOff />
|
||||
</IconButton>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
const { classes } = this.props;
|
||||
return (
|
||||
<Box className={classes.icon_box}>
|
||||
{this.props.isAdmin ? renderOnOff() : null}
|
||||
<IconButton color="primary" onClick={onRefresh}>
|
||||
<Refresh />
|
||||
</IconButton>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
const Sensors = this.props.device.sensors.map((sensor, index) => (
|
||||
<Grid item className={classes.grid_item} key={sensor.id}>
|
||||
<Grid container
|
||||
spacing={3}
|
||||
direction="row"
|
||||
justify="space-between"
|
||||
alignItems="center">
|
||||
<Grid item>
|
||||
<Typography className={classes.grid_item_typo}>Sensor {index}</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography className={classes.grid_item_typo_2}>{sensor.id}</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography style={this.getColor(sensor.status)}>Status: <b>{sensor.status}</b></Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
{this.renderSensorButtons(this.props.device, sensor)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
));
|
||||
|
||||
const handleChange = (panel) => (event, isExpanded) => {
|
||||
this.setState({ expanded: isExpanded ? panel : "" });
|
||||
};
|
||||
|
||||
return (
|
||||
<Accordion expanded={this.state.expanded === this.props.device.id} onChange={handleChange(this.props.device.id)}>
|
||||
<AccordionSummary className={classes.acc_summary}
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
aria-controls={"device-panel-/" + this.props.device.id}
|
||||
id={"device-panel-/" + this.props.device.id}>
|
||||
<Grid container
|
||||
spacing={3}
|
||||
direction="row"
|
||||
justify="space-between"
|
||||
alignItems="center">
|
||||
<Grid item>
|
||||
<Typography className={classes.grid_typo}>Device {this.props.index}</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography className={classes.grid_typo_2}>{this.props.device.id}</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography style={this.getColor(this.props.device.status)}>Status: <b>{this.props.device.status}</b></Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<FormControlLabel
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
onFocus={(event) => event.stopPropagation()}
|
||||
control={this.renderDeviceButtons(this.props.device)}/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails className={classes.acc_details}>
|
||||
<Grid className={classes.grid_item}
|
||||
container
|
||||
spacing={3}
|
||||
direction="column"
|
||||
justify="center"
|
||||
alignItems="flex-start">
|
||||
{Sensors}
|
||||
</Grid>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(withStyles(styles)(DeviceComponent));
|
File diff suppressed because one or more lines are too long
89
Birdmap.API/ClientApp/src/components/devices/Devices.jsx
Normal file
89
Birdmap.API/ClientApp/src/components/devices/Devices.jsx
Normal file
@ -0,0 +1,89 @@
|
||||
import { Box, Grid, IconButton, Paper, Typography } from '@material-ui/core';
|
||||
import { blueGrey } from '@material-ui/core/colors';
|
||||
import { Power, PowerOff, Refresh } from '@material-ui/icons/';
|
||||
import { withStyles } from '@material-ui/styles';
|
||||
import React from 'react';
|
||||
import DeviceService from '../../common/DeviceService';
|
||||
import DevicesContext from '../../contexts/DevicesContext';
|
||||
import DeviceComponent from './DeviceComponent';
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
padding: '64px',
|
||||
backgroundColor: theme.palette.primary.dark,
|
||||
},
|
||||
paper: {
|
||||
backgroundColor: blueGrey[50],
|
||||
height: '60px',
|
||||
},
|
||||
typo: {
|
||||
fontSize: theme.typography.pxToRem(20),
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
},
|
||||
});
|
||||
|
||||
class Devices extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
static contextType = DevicesContext;
|
||||
|
||||
componentDidMount() {
|
||||
}
|
||||
|
||||
renderButtons() {
|
||||
var service = new DeviceService();
|
||||
|
||||
const renderOnOff = () => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<IconButton color="primary" onClick={() => service.onlineall()}>
|
||||
<Power />
|
||||
</IconButton>
|
||||
<IconButton color="primary" onClick={() => service.offlineall()}>
|
||||
<PowerOff />
|
||||
</IconButton>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{this.props.isAdmin ? renderOnOff() : null}
|
||||
<IconButton color="primary" onClick={() => this.context.updateAllDevices()}>
|
||||
<Refresh />
|
||||
</IconButton>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
const Devices = this.context.devices.map((device, index) => (
|
||||
<DeviceComponent isAdmin={this.props.isAdmin} device={device} index={index} key={device.id}/>
|
||||
));
|
||||
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
<Paper className={classes.paper} square>
|
||||
<Grid container
|
||||
spacing={0}
|
||||
direction="row"
|
||||
justify="center"
|
||||
alignItems="center">
|
||||
<Grid item>
|
||||
<Typography className={classes.typo}>All Devices</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
{this.renderButtons()}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
{Devices}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(Devices);
|
@ -0,0 +1,61 @@
|
||||
import { Box, Tooltip } from '@material-ui/core';
|
||||
import { blue, red, yellow } from '@material-ui/core/colors';
|
||||
import RadioButtonCheckedIcon from '@material-ui/icons/RadioButtonChecked';
|
||||
import { makeStyles } from '@material-ui/styles';
|
||||
import React, { Component } from 'react';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
class DeviceMarker extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
AnchorEl: null
|
||||
}
|
||||
}
|
||||
|
||||
getColor() {
|
||||
const { device } = this.props;
|
||||
if (device.status === "Online") {
|
||||
return { color: blue[800] };
|
||||
} else if (device.status === "Offline") {
|
||||
return { color: yellow[800] };
|
||||
} else /* if (device.status == "unknown") */ {
|
||||
return { color: red[800] };
|
||||
}
|
||||
}
|
||||
|
||||
useStyles() {
|
||||
return makeStyles(theme => ({
|
||||
root: {
|
||||
display: 'grid'
|
||||
},
|
||||
icon: {
|
||||
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
render() {
|
||||
const classes = this.useStyles(this.props);
|
||||
const { device } = this.props;
|
||||
|
||||
const onClick = () => {
|
||||
this.props.history.push('/devices/' + device.id)
|
||||
};
|
||||
|
||||
const open = Boolean(this.state.AnchorEl);
|
||||
|
||||
return (
|
||||
<Box className={classes.root} boxShadow={5}>
|
||||
<Tooltip title={<div>ID: {device.id}<br />Status: {device.status}</div>} placement="top" leaveDelay={200} arrow>
|
||||
<RadioButtonCheckedIcon fontSize="small" style={this.getColor()}
|
||||
onClick={onClick}>
|
||||
</RadioButtonCheckedIcon>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(DeviceMarker);
|
124
Birdmap.API/ClientApp/src/components/heatmap/Heatmap.jsx
Normal file
124
Birdmap.API/ClientApp/src/components/heatmap/Heatmap.jsx
Normal file
@ -0,0 +1,124 @@
|
||||
/*global google*/
|
||||
import { Box, withStyles } from '@material-ui/core';
|
||||
import GoogleMapReact from 'google-map-react';
|
||||
import React, { Component } from 'react';
|
||||
import C from '../../common/Constants';
|
||||
import DevicesContext from '../../contexts/DevicesContext';
|
||||
import DeviceMarker from './DeviceMarker';
|
||||
|
||||
const lat_offset = 0.000038;
|
||||
const lng_offset = -0.000058;
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
height: '93vh',
|
||||
width: '100%',
|
||||
}
|
||||
});
|
||||
|
||||
class MapContainer extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
center: {
|
||||
lat: 48.275939, lng: 21.469640
|
||||
},
|
||||
heatmapPoints: [],
|
||||
};
|
||||
|
||||
this.probabilityHandler = this.probabilityHandler.bind(this);
|
||||
this.handlePoint = this.handlePoint.bind(this);
|
||||
}
|
||||
|
||||
static contextType = DevicesContext;
|
||||
|
||||
probabilityHandler(points) {
|
||||
for (var point of points) {
|
||||
this.handlePoint(point);
|
||||
}
|
||||
}
|
||||
|
||||
handlePoint(point) {
|
||||
if (point.prob > 0.5) {
|
||||
|
||||
this.setState({
|
||||
heatmapPoints: [...this.state.heatmapPoints, point]
|
||||
});
|
||||
|
||||
if (this._googleMap !== undefined) {
|
||||
const newPoint = { location: new google.maps.LatLng(point.lat, point.lng), weight: point.prob };
|
||||
if (this._googleMap.heatmap !== null) {
|
||||
this._googleMap.heatmap.data.push(newPoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.context.addHandler(C.probability_method_name, this.probabilityHandler);
|
||||
const newPoints = [];
|
||||
for (var p of this.context.heatmapPoints) {
|
||||
if (p.prob > 0.5) {
|
||||
newPoints.push(p)
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ heatmapPoints: newPoints });
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.context.removeHandler(C.probability_method_name, this.probabilityHandler);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {classes} = this.props;
|
||||
|
||||
const heatMapData = {
|
||||
positions: this.state.heatmapPoints,
|
||||
options: {
|
||||
radius: 50,
|
||||
opacity: 0.6,
|
||||
}
|
||||
}
|
||||
|
||||
const mapOptions = {
|
||||
disableDefaultUI: true,
|
||||
zoomControl: true,
|
||||
mapTypeControl: true,
|
||||
overviewMapControl: true,
|
||||
streetViewControl: false,
|
||||
scaleControl: true,
|
||||
mapTypeId: 'satellite'
|
||||
}
|
||||
|
||||
const Markers = this.context.devices.map((device, index) => (
|
||||
<DeviceMarker
|
||||
key={device.id}
|
||||
lat={device.coordinates.latitude + lat_offset}
|
||||
lng={device.coordinates.longitude + lng_offset}
|
||||
device={device}
|
||||
/>
|
||||
));
|
||||
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
<GoogleMapReact
|
||||
bootstrapURLKeys={{
|
||||
key: ["AIzaSyCZ51VFfxqZ2GkCmVrcNZdUKsM0fuBQUCY"],
|
||||
libraries: ['visualization']
|
||||
}}
|
||||
ref={(el) => this._googleMap = el}
|
||||
options={mapOptions}
|
||||
defaultZoom={18}
|
||||
heatmapLibrary={true}
|
||||
heatmap={heatMapData}
|
||||
defaultCenter={this.state.center}>
|
||||
{Markers}
|
||||
</GoogleMapReact>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(MapContainer);
|
@ -1,555 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.initMap = void 0;
|
||||
var google_maps_1 = require("google-maps");
|
||||
var map, heatmap;
|
||||
function initMap() {
|
||||
var options = { /* todo */};
|
||||
var loader = new google_maps_1.Loader('AIzaSyCZ51VFfxqZ2GkCmVrcNZdUKsM0fuBQUCY', options);
|
||||
loader.load().then(function (google) {
|
||||
map = new google.maps.Map(document.getElementById("map"), {
|
||||
zoom: 13,
|
||||
center: { lat: 37.775, lng: -122.434 },
|
||||
mapTypeId: "satellite",
|
||||
});
|
||||
heatmap = new google.maps.visualization.HeatmapLayer({
|
||||
data: getPoints(),
|
||||
map: map,
|
||||
});
|
||||
});
|
||||
}
|
||||
exports.initMap = initMap;
|
||||
function toggleHeatmap() {
|
||||
heatmap.setMap(heatmap.getMap() ? null : map);
|
||||
}
|
||||
function changeGradient() {
|
||||
var gradient = [
|
||||
"rgba(0, 255, 255, 0)",
|
||||
"rgba(0, 255, 255, 1)",
|
||||
"rgba(0, 191, 255, 1)",
|
||||
"rgba(0, 127, 255, 1)",
|
||||
"rgba(0, 63, 255, 1)",
|
||||
"rgba(0, 0, 255, 1)",
|
||||
"rgba(0, 0, 223, 1)",
|
||||
"rgba(0, 0, 191, 1)",
|
||||
"rgba(0, 0, 159, 1)",
|
||||
"rgba(0, 0, 127, 1)",
|
||||
"rgba(63, 0, 91, 1)",
|
||||
"rgba(127, 0, 63, 1)",
|
||||
"rgba(191, 0, 31, 1)",
|
||||
"rgba(255, 0, 0, 1)",
|
||||
];
|
||||
heatmap.set("gradient", heatmap.get("gradient") ? null : gradient);
|
||||
}
|
||||
function changeRadius() {
|
||||
heatmap.set("radius", heatmap.get("radius") ? null : 20);
|
||||
}
|
||||
function changeOpacity() {
|
||||
heatmap.set("opacity", heatmap.get("opacity") ? null : 0.2);
|
||||
}
|
||||
// Heatmap data: 500 Points
|
||||
function getPoints() {
|
||||
return [
|
||||
new google.maps.LatLng(37.782551, -122.445368),
|
||||
new google.maps.LatLng(37.782745, -122.444586),
|
||||
new google.maps.LatLng(37.782842, -122.443688),
|
||||
new google.maps.LatLng(37.782919, -122.442815),
|
||||
new google.maps.LatLng(37.782992, -122.442112),
|
||||
new google.maps.LatLng(37.7831, -122.441461),
|
||||
new google.maps.LatLng(37.783206, -122.440829),
|
||||
new google.maps.LatLng(37.783273, -122.440324),
|
||||
new google.maps.LatLng(37.783316, -122.440023),
|
||||
new google.maps.LatLng(37.783357, -122.439794),
|
||||
new google.maps.LatLng(37.783371, -122.439687),
|
||||
new google.maps.LatLng(37.783368, -122.439666),
|
||||
new google.maps.LatLng(37.783383, -122.439594),
|
||||
new google.maps.LatLng(37.783508, -122.439525),
|
||||
new google.maps.LatLng(37.783842, -122.439591),
|
||||
new google.maps.LatLng(37.784147, -122.439668),
|
||||
new google.maps.LatLng(37.784206, -122.439686),
|
||||
new google.maps.LatLng(37.784386, -122.43979),
|
||||
new google.maps.LatLng(37.784701, -122.439902),
|
||||
new google.maps.LatLng(37.784965, -122.439938),
|
||||
new google.maps.LatLng(37.78501, -122.439947),
|
||||
new google.maps.LatLng(37.78536, -122.439952),
|
||||
new google.maps.LatLng(37.785715, -122.44003),
|
||||
new google.maps.LatLng(37.786117, -122.440119),
|
||||
new google.maps.LatLng(37.786564, -122.440209),
|
||||
new google.maps.LatLng(37.786905, -122.44027),
|
||||
new google.maps.LatLng(37.786956, -122.440279),
|
||||
new google.maps.LatLng(37.800224, -122.43352),
|
||||
new google.maps.LatLng(37.800155, -122.434101),
|
||||
new google.maps.LatLng(37.80016, -122.43443),
|
||||
new google.maps.LatLng(37.800378, -122.434527),
|
||||
new google.maps.LatLng(37.800738, -122.434598),
|
||||
new google.maps.LatLng(37.800938, -122.43465),
|
||||
new google.maps.LatLng(37.801024, -122.434889),
|
||||
new google.maps.LatLng(37.800955, -122.435392),
|
||||
new google.maps.LatLng(37.800886, -122.435959),
|
||||
new google.maps.LatLng(37.800811, -122.436275),
|
||||
new google.maps.LatLng(37.800788, -122.436299),
|
||||
new google.maps.LatLng(37.800719, -122.436302),
|
||||
new google.maps.LatLng(37.800702, -122.436298),
|
||||
new google.maps.LatLng(37.800661, -122.436273),
|
||||
new google.maps.LatLng(37.800395, -122.436172),
|
||||
new google.maps.LatLng(37.800228, -122.436116),
|
||||
new google.maps.LatLng(37.800169, -122.43613),
|
||||
new google.maps.LatLng(37.800066, -122.436167),
|
||||
new google.maps.LatLng(37.784345, -122.422922),
|
||||
new google.maps.LatLng(37.784389, -122.422926),
|
||||
new google.maps.LatLng(37.784437, -122.422924),
|
||||
new google.maps.LatLng(37.784746, -122.422818),
|
||||
new google.maps.LatLng(37.785436, -122.422959),
|
||||
new google.maps.LatLng(37.78612, -122.423112),
|
||||
new google.maps.LatLng(37.786433, -122.423029),
|
||||
new google.maps.LatLng(37.786631, -122.421213),
|
||||
new google.maps.LatLng(37.78666, -122.421033),
|
||||
new google.maps.LatLng(37.786801, -122.420141),
|
||||
new google.maps.LatLng(37.786823, -122.420034),
|
||||
new google.maps.LatLng(37.786831, -122.419916),
|
||||
new google.maps.LatLng(37.787034, -122.418208),
|
||||
new google.maps.LatLng(37.787056, -122.418034),
|
||||
new google.maps.LatLng(37.787169, -122.417145),
|
||||
new google.maps.LatLng(37.787217, -122.416715),
|
||||
new google.maps.LatLng(37.786144, -122.416403),
|
||||
new google.maps.LatLng(37.785292, -122.416257),
|
||||
new google.maps.LatLng(37.780666, -122.390374),
|
||||
new google.maps.LatLng(37.780501, -122.391281),
|
||||
new google.maps.LatLng(37.780148, -122.392052),
|
||||
new google.maps.LatLng(37.780173, -122.391148),
|
||||
new google.maps.LatLng(37.780693, -122.390592),
|
||||
new google.maps.LatLng(37.781261, -122.391142),
|
||||
new google.maps.LatLng(37.781808, -122.39173),
|
||||
new google.maps.LatLng(37.78234, -122.392341),
|
||||
new google.maps.LatLng(37.782812, -122.393022),
|
||||
new google.maps.LatLng(37.7833, -122.393672),
|
||||
new google.maps.LatLng(37.783809, -122.394275),
|
||||
new google.maps.LatLng(37.784246, -122.394979),
|
||||
new google.maps.LatLng(37.784791, -122.395958),
|
||||
new google.maps.LatLng(37.785675, -122.396746),
|
||||
new google.maps.LatLng(37.786262, -122.39578),
|
||||
new google.maps.LatLng(37.786776, -122.395093),
|
||||
new google.maps.LatLng(37.787282, -122.394426),
|
||||
new google.maps.LatLng(37.787783, -122.393767),
|
||||
new google.maps.LatLng(37.788343, -122.393184),
|
||||
new google.maps.LatLng(37.788895, -122.392506),
|
||||
new google.maps.LatLng(37.789371, -122.391701),
|
||||
new google.maps.LatLng(37.789722, -122.390952),
|
||||
new google.maps.LatLng(37.790315, -122.390305),
|
||||
new google.maps.LatLng(37.790738, -122.389616),
|
||||
new google.maps.LatLng(37.779448, -122.438702),
|
||||
new google.maps.LatLng(37.779023, -122.438585),
|
||||
new google.maps.LatLng(37.778542, -122.438492),
|
||||
new google.maps.LatLng(37.7781, -122.438411),
|
||||
new google.maps.LatLng(37.777986, -122.438376),
|
||||
new google.maps.LatLng(37.77768, -122.438313),
|
||||
new google.maps.LatLng(37.777316, -122.438273),
|
||||
new google.maps.LatLng(37.777135, -122.438254),
|
||||
new google.maps.LatLng(37.776987, -122.438303),
|
||||
new google.maps.LatLng(37.776946, -122.438404),
|
||||
new google.maps.LatLng(37.776944, -122.438467),
|
||||
new google.maps.LatLng(37.776892, -122.438459),
|
||||
new google.maps.LatLng(37.776842, -122.438442),
|
||||
new google.maps.LatLng(37.776822, -122.438391),
|
||||
new google.maps.LatLng(37.776814, -122.438412),
|
||||
new google.maps.LatLng(37.776787, -122.438628),
|
||||
new google.maps.LatLng(37.776729, -122.43865),
|
||||
new google.maps.LatLng(37.776759, -122.438677),
|
||||
new google.maps.LatLng(37.776772, -122.438498),
|
||||
new google.maps.LatLng(37.776787, -122.438389),
|
||||
new google.maps.LatLng(37.776848, -122.438283),
|
||||
new google.maps.LatLng(37.77687, -122.438239),
|
||||
new google.maps.LatLng(37.777015, -122.438198),
|
||||
new google.maps.LatLng(37.777333, -122.438256),
|
||||
new google.maps.LatLng(37.777595, -122.438308),
|
||||
new google.maps.LatLng(37.777797, -122.438344),
|
||||
new google.maps.LatLng(37.77816, -122.438442),
|
||||
new google.maps.LatLng(37.778414, -122.438508),
|
||||
new google.maps.LatLng(37.778445, -122.438516),
|
||||
new google.maps.LatLng(37.778503, -122.438529),
|
||||
new google.maps.LatLng(37.778607, -122.438549),
|
||||
new google.maps.LatLng(37.77867, -122.438644),
|
||||
new google.maps.LatLng(37.778847, -122.438706),
|
||||
new google.maps.LatLng(37.77924, -122.438744),
|
||||
new google.maps.LatLng(37.779738, -122.438822),
|
||||
new google.maps.LatLng(37.780201, -122.438882),
|
||||
new google.maps.LatLng(37.7804, -122.438905),
|
||||
new google.maps.LatLng(37.780501, -122.438921),
|
||||
new google.maps.LatLng(37.780892, -122.438986),
|
||||
new google.maps.LatLng(37.781446, -122.439087),
|
||||
new google.maps.LatLng(37.781985, -122.439199),
|
||||
new google.maps.LatLng(37.782239, -122.439249),
|
||||
new google.maps.LatLng(37.782286, -122.439266),
|
||||
new google.maps.LatLng(37.797847, -122.429388),
|
||||
new google.maps.LatLng(37.797874, -122.42918),
|
||||
new google.maps.LatLng(37.797885, -122.429069),
|
||||
new google.maps.LatLng(37.797887, -122.42905),
|
||||
new google.maps.LatLng(37.797933, -122.428954),
|
||||
new google.maps.LatLng(37.798242, -122.42899),
|
||||
new google.maps.LatLng(37.798617, -122.429075),
|
||||
new google.maps.LatLng(37.798719, -122.429092),
|
||||
new google.maps.LatLng(37.798944, -122.429145),
|
||||
new google.maps.LatLng(37.79932, -122.429251),
|
||||
new google.maps.LatLng(37.79959, -122.429309),
|
||||
new google.maps.LatLng(37.799677, -122.429324),
|
||||
new google.maps.LatLng(37.799966, -122.42936),
|
||||
new google.maps.LatLng(37.800288, -122.42943),
|
||||
new google.maps.LatLng(37.800443, -122.429461),
|
||||
new google.maps.LatLng(37.800465, -122.429474),
|
||||
new google.maps.LatLng(37.800644, -122.42954),
|
||||
new google.maps.LatLng(37.800948, -122.42962),
|
||||
new google.maps.LatLng(37.801242, -122.429685),
|
||||
new google.maps.LatLng(37.801375, -122.429702),
|
||||
new google.maps.LatLng(37.8014, -122.429703),
|
||||
new google.maps.LatLng(37.801453, -122.429707),
|
||||
new google.maps.LatLng(37.801473, -122.429709),
|
||||
new google.maps.LatLng(37.801532, -122.429707),
|
||||
new google.maps.LatLng(37.801852, -122.429729),
|
||||
new google.maps.LatLng(37.802173, -122.429789),
|
||||
new google.maps.LatLng(37.802459, -122.429847),
|
||||
new google.maps.LatLng(37.802554, -122.429825),
|
||||
new google.maps.LatLng(37.802647, -122.429549),
|
||||
new google.maps.LatLng(37.802693, -122.429179),
|
||||
new google.maps.LatLng(37.802729, -122.428751),
|
||||
new google.maps.LatLng(37.766104, -122.409291),
|
||||
new google.maps.LatLng(37.766103, -122.409268),
|
||||
new google.maps.LatLng(37.766138, -122.409229),
|
||||
new google.maps.LatLng(37.766183, -122.409231),
|
||||
new google.maps.LatLng(37.766153, -122.409276),
|
||||
new google.maps.LatLng(37.766005, -122.409365),
|
||||
new google.maps.LatLng(37.765897, -122.40957),
|
||||
new google.maps.LatLng(37.765767, -122.409739),
|
||||
new google.maps.LatLng(37.765693, -122.410389),
|
||||
new google.maps.LatLng(37.765615, -122.411201),
|
||||
new google.maps.LatLng(37.765533, -122.412121),
|
||||
new google.maps.LatLng(37.765467, -122.412939),
|
||||
new google.maps.LatLng(37.765444, -122.414821),
|
||||
new google.maps.LatLng(37.765444, -122.414964),
|
||||
new google.maps.LatLng(37.765318, -122.415424),
|
||||
new google.maps.LatLng(37.763961, -122.415296),
|
||||
new google.maps.LatLng(37.763115, -122.415196),
|
||||
new google.maps.LatLng(37.762967, -122.415183),
|
||||
new google.maps.LatLng(37.762278, -122.415127),
|
||||
new google.maps.LatLng(37.761675, -122.415055),
|
||||
new google.maps.LatLng(37.760932, -122.414988),
|
||||
new google.maps.LatLng(37.759337, -122.414862),
|
||||
new google.maps.LatLng(37.773187, -122.421922),
|
||||
new google.maps.LatLng(37.773043, -122.422118),
|
||||
new google.maps.LatLng(37.773007, -122.422165),
|
||||
new google.maps.LatLng(37.772979, -122.422219),
|
||||
new google.maps.LatLng(37.772865, -122.422394),
|
||||
new google.maps.LatLng(37.772779, -122.422503),
|
||||
new google.maps.LatLng(37.772676, -122.422701),
|
||||
new google.maps.LatLng(37.772606, -122.422806),
|
||||
new google.maps.LatLng(37.772566, -122.42284),
|
||||
new google.maps.LatLng(37.772508, -122.422852),
|
||||
new google.maps.LatLng(37.772387, -122.423011),
|
||||
new google.maps.LatLng(37.772099, -122.423328),
|
||||
new google.maps.LatLng(37.771704, -122.423783),
|
||||
new google.maps.LatLng(37.771481, -122.424081),
|
||||
new google.maps.LatLng(37.7714, -122.424179),
|
||||
new google.maps.LatLng(37.771352, -122.42422),
|
||||
new google.maps.LatLng(37.771248, -122.424327),
|
||||
new google.maps.LatLng(37.770904, -122.424781),
|
||||
new google.maps.LatLng(37.77052, -122.425283),
|
||||
new google.maps.LatLng(37.770337, -122.425553),
|
||||
new google.maps.LatLng(37.770128, -122.425832),
|
||||
new google.maps.LatLng(37.769756, -122.426331),
|
||||
new google.maps.LatLng(37.7693, -122.426902),
|
||||
new google.maps.LatLng(37.769132, -122.427065),
|
||||
new google.maps.LatLng(37.769092, -122.427103),
|
||||
new google.maps.LatLng(37.768979, -122.427172),
|
||||
new google.maps.LatLng(37.768595, -122.427634),
|
||||
new google.maps.LatLng(37.768372, -122.427913),
|
||||
new google.maps.LatLng(37.768337, -122.427961),
|
||||
new google.maps.LatLng(37.768244, -122.428138),
|
||||
new google.maps.LatLng(37.767942, -122.428581),
|
||||
new google.maps.LatLng(37.767482, -122.429094),
|
||||
new google.maps.LatLng(37.767031, -122.429606),
|
||||
new google.maps.LatLng(37.766732, -122.429986),
|
||||
new google.maps.LatLng(37.76668, -122.430058),
|
||||
new google.maps.LatLng(37.766633, -122.430109),
|
||||
new google.maps.LatLng(37.76658, -122.430211),
|
||||
new google.maps.LatLng(37.766367, -122.430594),
|
||||
new google.maps.LatLng(37.76591, -122.431137),
|
||||
new google.maps.LatLng(37.765353, -122.431806),
|
||||
new google.maps.LatLng(37.764962, -122.432298),
|
||||
new google.maps.LatLng(37.764868, -122.432486),
|
||||
new google.maps.LatLng(37.764518, -122.432913),
|
||||
new google.maps.LatLng(37.763435, -122.434173),
|
||||
new google.maps.LatLng(37.762847, -122.434953),
|
||||
new google.maps.LatLng(37.762291, -122.435935),
|
||||
new google.maps.LatLng(37.762224, -122.436074),
|
||||
new google.maps.LatLng(37.761957, -122.436892),
|
||||
new google.maps.LatLng(37.761652, -122.438886),
|
||||
new google.maps.LatLng(37.761284, -122.439955),
|
||||
new google.maps.LatLng(37.76121, -122.440068),
|
||||
new google.maps.LatLng(37.761064, -122.44072),
|
||||
new google.maps.LatLng(37.76104, -122.441411),
|
||||
new google.maps.LatLng(37.761048, -122.442324),
|
||||
new google.maps.LatLng(37.760851, -122.443118),
|
||||
new google.maps.LatLng(37.759977, -122.444591),
|
||||
new google.maps.LatLng(37.759913, -122.444698),
|
||||
new google.maps.LatLng(37.759623, -122.445065),
|
||||
new google.maps.LatLng(37.758902, -122.445158),
|
||||
new google.maps.LatLng(37.758428, -122.44457),
|
||||
new google.maps.LatLng(37.757687, -122.44334),
|
||||
new google.maps.LatLng(37.757583, -122.44324),
|
||||
new google.maps.LatLng(37.757019, -122.442787),
|
||||
new google.maps.LatLng(37.756603, -122.442322),
|
||||
new google.maps.LatLng(37.75638, -122.441602),
|
||||
new google.maps.LatLng(37.75579, -122.441382),
|
||||
new google.maps.LatLng(37.754493, -122.442133),
|
||||
new google.maps.LatLng(37.754361, -122.442206),
|
||||
new google.maps.LatLng(37.753719, -122.44265),
|
||||
new google.maps.LatLng(37.753096, -122.442915),
|
||||
new google.maps.LatLng(37.751617, -122.443211),
|
||||
new google.maps.LatLng(37.751496, -122.443246),
|
||||
new google.maps.LatLng(37.750733, -122.443428),
|
||||
new google.maps.LatLng(37.750126, -122.443536),
|
||||
new google.maps.LatLng(37.750103, -122.443784),
|
||||
new google.maps.LatLng(37.75039, -122.44401),
|
||||
new google.maps.LatLng(37.750448, -122.444013),
|
||||
new google.maps.LatLng(37.750536, -122.44404),
|
||||
new google.maps.LatLng(37.750493, -122.444141),
|
||||
new google.maps.LatLng(37.790859, -122.402808),
|
||||
new google.maps.LatLng(37.790864, -122.402768),
|
||||
new google.maps.LatLng(37.790995, -122.402539),
|
||||
new google.maps.LatLng(37.791148, -122.402172),
|
||||
new google.maps.LatLng(37.791385, -122.401312),
|
||||
new google.maps.LatLng(37.791405, -122.400776),
|
||||
new google.maps.LatLng(37.791288, -122.400528),
|
||||
new google.maps.LatLng(37.791113, -122.400441),
|
||||
new google.maps.LatLng(37.791027, -122.400395),
|
||||
new google.maps.LatLng(37.791094, -122.400311),
|
||||
new google.maps.LatLng(37.791211, -122.400183),
|
||||
new google.maps.LatLng(37.79106, -122.399334),
|
||||
new google.maps.LatLng(37.790538, -122.398718),
|
||||
new google.maps.LatLng(37.790095, -122.398086),
|
||||
new google.maps.LatLng(37.789644, -122.39736),
|
||||
new google.maps.LatLng(37.789254, -122.396844),
|
||||
new google.maps.LatLng(37.788855, -122.396397),
|
||||
new google.maps.LatLng(37.788483, -122.395963),
|
||||
new google.maps.LatLng(37.788015, -122.395365),
|
||||
new google.maps.LatLng(37.787558, -122.394735),
|
||||
new google.maps.LatLng(37.787472, -122.394323),
|
||||
new google.maps.LatLng(37.78763, -122.394025),
|
||||
new google.maps.LatLng(37.787767, -122.393987),
|
||||
new google.maps.LatLng(37.787486, -122.394452),
|
||||
new google.maps.LatLng(37.786977, -122.395043),
|
||||
new google.maps.LatLng(37.786583, -122.395552),
|
||||
new google.maps.LatLng(37.78654, -122.39561),
|
||||
new google.maps.LatLng(37.786516, -122.395659),
|
||||
new google.maps.LatLng(37.786378, -122.395707),
|
||||
new google.maps.LatLng(37.786044, -122.395362),
|
||||
new google.maps.LatLng(37.785598, -122.394715),
|
||||
new google.maps.LatLng(37.785321, -122.394361),
|
||||
new google.maps.LatLng(37.785207, -122.394236),
|
||||
new google.maps.LatLng(37.785751, -122.394062),
|
||||
new google.maps.LatLng(37.785996, -122.393881),
|
||||
new google.maps.LatLng(37.786092, -122.39383),
|
||||
new google.maps.LatLng(37.785998, -122.393899),
|
||||
new google.maps.LatLng(37.785114, -122.394365),
|
||||
new google.maps.LatLng(37.785022, -122.394441),
|
||||
new google.maps.LatLng(37.784823, -122.394635),
|
||||
new google.maps.LatLng(37.784719, -122.394629),
|
||||
new google.maps.LatLng(37.785069, -122.394176),
|
||||
new google.maps.LatLng(37.7855, -122.39365),
|
||||
new google.maps.LatLng(37.78577, -122.393291),
|
||||
new google.maps.LatLng(37.785839, -122.393159),
|
||||
new google.maps.LatLng(37.782651, -122.400628),
|
||||
new google.maps.LatLng(37.782616, -122.400599),
|
||||
new google.maps.LatLng(37.782702, -122.40047),
|
||||
new google.maps.LatLng(37.782915, -122.400192),
|
||||
new google.maps.LatLng(37.783137, -122.399887),
|
||||
new google.maps.LatLng(37.783414, -122.399519),
|
||||
new google.maps.LatLng(37.783629, -122.399237),
|
||||
new google.maps.LatLng(37.783688, -122.399157),
|
||||
new google.maps.LatLng(37.783716, -122.399106),
|
||||
new google.maps.LatLng(37.783798, -122.399072),
|
||||
new google.maps.LatLng(37.783997, -122.399186),
|
||||
new google.maps.LatLng(37.784271, -122.399538),
|
||||
new google.maps.LatLng(37.784577, -122.399948),
|
||||
new google.maps.LatLng(37.784828, -122.40026),
|
||||
new google.maps.LatLng(37.784999, -122.400477),
|
||||
new google.maps.LatLng(37.785113, -122.400651),
|
||||
new google.maps.LatLng(37.785155, -122.400703),
|
||||
new google.maps.LatLng(37.785192, -122.400749),
|
||||
new google.maps.LatLng(37.785278, -122.400839),
|
||||
new google.maps.LatLng(37.785387, -122.400857),
|
||||
new google.maps.LatLng(37.785478, -122.40089),
|
||||
new google.maps.LatLng(37.785526, -122.401022),
|
||||
new google.maps.LatLng(37.785598, -122.401148),
|
||||
new google.maps.LatLng(37.785631, -122.401202),
|
||||
new google.maps.LatLng(37.78566, -122.401267),
|
||||
new google.maps.LatLng(37.803986, -122.426035),
|
||||
new google.maps.LatLng(37.804102, -122.425089),
|
||||
new google.maps.LatLng(37.804211, -122.424156),
|
||||
new google.maps.LatLng(37.803861, -122.423385),
|
||||
new google.maps.LatLng(37.803151, -122.423214),
|
||||
new google.maps.LatLng(37.802439, -122.423077),
|
||||
new google.maps.LatLng(37.80174, -122.422905),
|
||||
new google.maps.LatLng(37.801069, -122.422785),
|
||||
new google.maps.LatLng(37.800345, -122.422649),
|
||||
new google.maps.LatLng(37.799633, -122.422603),
|
||||
new google.maps.LatLng(37.79975, -122.4217),
|
||||
new google.maps.LatLng(37.799885, -122.420854),
|
||||
new google.maps.LatLng(37.799209, -122.420607),
|
||||
new google.maps.LatLng(37.795656, -122.400395),
|
||||
new google.maps.LatLng(37.795203, -122.400304),
|
||||
new google.maps.LatLng(37.778738, -122.415584),
|
||||
new google.maps.LatLng(37.778812, -122.415189),
|
||||
new google.maps.LatLng(37.778824, -122.415092),
|
||||
new google.maps.LatLng(37.778833, -122.414932),
|
||||
new google.maps.LatLng(37.778834, -122.414898),
|
||||
new google.maps.LatLng(37.77874, -122.414757),
|
||||
new google.maps.LatLng(37.778501, -122.414433),
|
||||
new google.maps.LatLng(37.778182, -122.414026),
|
||||
new google.maps.LatLng(37.777851, -122.413623),
|
||||
new google.maps.LatLng(37.777486, -122.413166),
|
||||
new google.maps.LatLng(37.777109, -122.412674),
|
||||
new google.maps.LatLng(37.776743, -122.412186),
|
||||
new google.maps.LatLng(37.77644, -122.4118),
|
||||
new google.maps.LatLng(37.776295, -122.411614),
|
||||
new google.maps.LatLng(37.776158, -122.41144),
|
||||
new google.maps.LatLng(37.775806, -122.410997),
|
||||
new google.maps.LatLng(37.775422, -122.410484),
|
||||
new google.maps.LatLng(37.775126, -122.410087),
|
||||
new google.maps.LatLng(37.775012, -122.409854),
|
||||
new google.maps.LatLng(37.775164, -122.409573),
|
||||
new google.maps.LatLng(37.775498, -122.40918),
|
||||
new google.maps.LatLng(37.775868, -122.40873),
|
||||
new google.maps.LatLng(37.776256, -122.40824),
|
||||
new google.maps.LatLng(37.776519, -122.407928),
|
||||
new google.maps.LatLng(37.776539, -122.407904),
|
||||
new google.maps.LatLng(37.776595, -122.407854),
|
||||
new google.maps.LatLng(37.776853, -122.407547),
|
||||
new google.maps.LatLng(37.777234, -122.407087),
|
||||
new google.maps.LatLng(37.777644, -122.406558),
|
||||
new google.maps.LatLng(37.778066, -122.406017),
|
||||
new google.maps.LatLng(37.778468, -122.405499),
|
||||
new google.maps.LatLng(37.778866, -122.404995),
|
||||
new google.maps.LatLng(37.779295, -122.404455),
|
||||
new google.maps.LatLng(37.779695, -122.40395),
|
||||
new google.maps.LatLng(37.779982, -122.403584),
|
||||
new google.maps.LatLng(37.780295, -122.403223),
|
||||
new google.maps.LatLng(37.780664, -122.402766),
|
||||
new google.maps.LatLng(37.781043, -122.402288),
|
||||
new google.maps.LatLng(37.781399, -122.401823),
|
||||
new google.maps.LatLng(37.781727, -122.401407),
|
||||
new google.maps.LatLng(37.781853, -122.401247),
|
||||
new google.maps.LatLng(37.781894, -122.401195),
|
||||
new google.maps.LatLng(37.782076, -122.400977),
|
||||
new google.maps.LatLng(37.782338, -122.400603),
|
||||
new google.maps.LatLng(37.782666, -122.400133),
|
||||
new google.maps.LatLng(37.783048, -122.399634),
|
||||
new google.maps.LatLng(37.78345, -122.399198),
|
||||
new google.maps.LatLng(37.783791, -122.398998),
|
||||
new google.maps.LatLng(37.784177, -122.398959),
|
||||
new google.maps.LatLng(37.784388, -122.398971),
|
||||
new google.maps.LatLng(37.784404, -122.399128),
|
||||
new google.maps.LatLng(37.784586, -122.399524),
|
||||
new google.maps.LatLng(37.784835, -122.399927),
|
||||
new google.maps.LatLng(37.785116, -122.400307),
|
||||
new google.maps.LatLng(37.785282, -122.400539),
|
||||
new google.maps.LatLng(37.785346, -122.400692),
|
||||
new google.maps.LatLng(37.765769, -122.407201),
|
||||
new google.maps.LatLng(37.76579, -122.407414),
|
||||
new google.maps.LatLng(37.765802, -122.407755),
|
||||
new google.maps.LatLng(37.765791, -122.408219),
|
||||
new google.maps.LatLng(37.765763, -122.408759),
|
||||
new google.maps.LatLng(37.765726, -122.409348),
|
||||
new google.maps.LatLng(37.765716, -122.409882),
|
||||
new google.maps.LatLng(37.765708, -122.410202),
|
||||
new google.maps.LatLng(37.765705, -122.410253),
|
||||
new google.maps.LatLng(37.765707, -122.410369),
|
||||
new google.maps.LatLng(37.765692, -122.41072),
|
||||
new google.maps.LatLng(37.765699, -122.411215),
|
||||
new google.maps.LatLng(37.765687, -122.411789),
|
||||
new google.maps.LatLng(37.765666, -122.412373),
|
||||
new google.maps.LatLng(37.765598, -122.412883),
|
||||
new google.maps.LatLng(37.765543, -122.413039),
|
||||
new google.maps.LatLng(37.765532, -122.413125),
|
||||
new google.maps.LatLng(37.7655, -122.413553),
|
||||
new google.maps.LatLng(37.765448, -122.414053),
|
||||
new google.maps.LatLng(37.765388, -122.414645),
|
||||
new google.maps.LatLng(37.765323, -122.41525),
|
||||
new google.maps.LatLng(37.765303, -122.415847),
|
||||
new google.maps.LatLng(37.765251, -122.416439),
|
||||
new google.maps.LatLng(37.765204, -122.41702),
|
||||
new google.maps.LatLng(37.765172, -122.417556),
|
||||
new google.maps.LatLng(37.765164, -122.418075),
|
||||
new google.maps.LatLng(37.765153, -122.418618),
|
||||
new google.maps.LatLng(37.765136, -122.419112),
|
||||
new google.maps.LatLng(37.765129, -122.419378),
|
||||
new google.maps.LatLng(37.765119, -122.419481),
|
||||
new google.maps.LatLng(37.7651, -122.419852),
|
||||
new google.maps.LatLng(37.765083, -122.420349),
|
||||
new google.maps.LatLng(37.765045, -122.42093),
|
||||
new google.maps.LatLng(37.764992, -122.421481),
|
||||
new google.maps.LatLng(37.76498, -122.421695),
|
||||
new google.maps.LatLng(37.764993, -122.421843),
|
||||
new google.maps.LatLng(37.764986, -122.422255),
|
||||
new google.maps.LatLng(37.764975, -122.422823),
|
||||
new google.maps.LatLng(37.764939, -122.423411),
|
||||
new google.maps.LatLng(37.764902, -122.424014),
|
||||
new google.maps.LatLng(37.764853, -122.424576),
|
||||
new google.maps.LatLng(37.764826, -122.424922),
|
||||
new google.maps.LatLng(37.764796, -122.425375),
|
||||
new google.maps.LatLng(37.764782, -122.425869),
|
||||
new google.maps.LatLng(37.764768, -122.426089),
|
||||
new google.maps.LatLng(37.764766, -122.426117),
|
||||
new google.maps.LatLng(37.764723, -122.426276),
|
||||
new google.maps.LatLng(37.764681, -122.426649),
|
||||
new google.maps.LatLng(37.782012, -122.4042),
|
||||
new google.maps.LatLng(37.781574, -122.404911),
|
||||
new google.maps.LatLng(37.781055, -122.405597),
|
||||
new google.maps.LatLng(37.780479, -122.406341),
|
||||
new google.maps.LatLng(37.779996, -122.406939),
|
||||
new google.maps.LatLng(37.779459, -122.407613),
|
||||
new google.maps.LatLng(37.778953, -122.408228),
|
||||
new google.maps.LatLng(37.778409, -122.408839),
|
||||
new google.maps.LatLng(37.777842, -122.409501),
|
||||
new google.maps.LatLng(37.777334, -122.410181),
|
||||
new google.maps.LatLng(37.776809, -122.410836),
|
||||
new google.maps.LatLng(37.77624, -122.411514),
|
||||
new google.maps.LatLng(37.775725, -122.412145),
|
||||
new google.maps.LatLng(37.77519, -122.412805),
|
||||
new google.maps.LatLng(37.774672, -122.413464),
|
||||
new google.maps.LatLng(37.774084, -122.414186),
|
||||
new google.maps.LatLng(37.773533, -122.413636),
|
||||
new google.maps.LatLng(37.773021, -122.413009),
|
||||
new google.maps.LatLng(37.772501, -122.412371),
|
||||
new google.maps.LatLng(37.771964, -122.411681),
|
||||
new google.maps.LatLng(37.771479, -122.411078),
|
||||
new google.maps.LatLng(37.770992, -122.410477),
|
||||
new google.maps.LatLng(37.770467, -122.409801),
|
||||
new google.maps.LatLng(37.77009, -122.408904),
|
||||
new google.maps.LatLng(37.769657, -122.408103),
|
||||
new google.maps.LatLng(37.769132, -122.407276),
|
||||
new google.maps.LatLng(37.768564, -122.406469),
|
||||
new google.maps.LatLng(37.76798, -122.405745),
|
||||
new google.maps.LatLng(37.76738, -122.405299),
|
||||
new google.maps.LatLng(37.766604, -122.405297),
|
||||
new google.maps.LatLng(37.765838, -122.4052),
|
||||
new google.maps.LatLng(37.765139, -122.405139),
|
||||
new google.maps.LatLng(37.764457, -122.405094),
|
||||
new google.maps.LatLng(37.763716, -122.405142),
|
||||
new google.maps.LatLng(37.762932, -122.405398),
|
||||
new google.maps.LatLng(37.762126, -122.405813),
|
||||
new google.maps.LatLng(37.761344, -122.406215),
|
||||
new google.maps.LatLng(37.760556, -122.406495),
|
||||
new google.maps.LatLng(37.759732, -122.406484),
|
||||
new google.maps.LatLng(37.75891, -122.406228),
|
||||
new google.maps.LatLng(37.758182, -122.405695),
|
||||
new google.maps.LatLng(37.757676, -122.405118),
|
||||
new google.maps.LatLng(37.757039, -122.404346),
|
||||
new google.maps.LatLng(37.756335, -122.403719),
|
||||
new google.maps.LatLng(37.755503, -122.403406),
|
||||
new google.maps.LatLng(37.754665, -122.403242),
|
||||
new google.maps.LatLng(37.753837, -122.403172),
|
||||
new google.maps.LatLng(37.752986, -122.403112),
|
||||
new google.maps.LatLng(37.751266, -122.403355),
|
||||
];
|
||||
}
|
||||
//# sourceMappingURL=heatmap.js.map
|
File diff suppressed because one or more lines are too long
@ -1,561 +0,0 @@
|
||||
import { Loader, LoaderOptions } from 'google-maps';
|
||||
|
||||
let map: google.maps.Map, heatmap: google.maps.visualization.HeatmapLayer;
|
||||
|
||||
function initMap(): void {
|
||||
const options: LoaderOptions = {/* todo */ };
|
||||
const loader = new Loader('AIzaSyCZ51VFfxqZ2GkCmVrcNZdUKsM0fuBQUCY', options);
|
||||
|
||||
loader.load().then(function (google) {
|
||||
map = new google.maps.Map(document.getElementById("map") as HTMLElement, {
|
||||
zoom: 13,
|
||||
center: { lat: 37.775, lng: -122.434 },
|
||||
mapTypeId: "satellite",
|
||||
});
|
||||
|
||||
heatmap = new google.maps.visualization.HeatmapLayer({
|
||||
data: getPoints(),
|
||||
map: map,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function toggleHeatmap() {
|
||||
heatmap.setMap(heatmap.getMap() ? null : map);
|
||||
}
|
||||
|
||||
function changeGradient() {
|
||||
const gradient = [
|
||||
"rgba(0, 255, 255, 0)",
|
||||
"rgba(0, 255, 255, 1)",
|
||||
"rgba(0, 191, 255, 1)",
|
||||
"rgba(0, 127, 255, 1)",
|
||||
"rgba(0, 63, 255, 1)",
|
||||
"rgba(0, 0, 255, 1)",
|
||||
"rgba(0, 0, 223, 1)",
|
||||
"rgba(0, 0, 191, 1)",
|
||||
"rgba(0, 0, 159, 1)",
|
||||
"rgba(0, 0, 127, 1)",
|
||||
"rgba(63, 0, 91, 1)",
|
||||
"rgba(127, 0, 63, 1)",
|
||||
"rgba(191, 0, 31, 1)",
|
||||
"rgba(255, 0, 0, 1)",
|
||||
];
|
||||
heatmap.set("gradient", heatmap.get("gradient") ? null : gradient);
|
||||
}
|
||||
|
||||
function changeRadius() {
|
||||
heatmap.set("radius", heatmap.get("radius") ? null : 20);
|
||||
}
|
||||
|
||||
function changeOpacity() {
|
||||
heatmap.set("opacity", heatmap.get("opacity") ? null : 0.2);
|
||||
}
|
||||
|
||||
// Heatmap data: 500 Points
|
||||
function getPoints() {
|
||||
return [
|
||||
new google.maps.LatLng(37.782551, -122.445368),
|
||||
new google.maps.LatLng(37.782745, -122.444586),
|
||||
new google.maps.LatLng(37.782842, -122.443688),
|
||||
new google.maps.LatLng(37.782919, -122.442815),
|
||||
new google.maps.LatLng(37.782992, -122.442112),
|
||||
new google.maps.LatLng(37.7831, -122.441461),
|
||||
new google.maps.LatLng(37.783206, -122.440829),
|
||||
new google.maps.LatLng(37.783273, -122.440324),
|
||||
new google.maps.LatLng(37.783316, -122.440023),
|
||||
new google.maps.LatLng(37.783357, -122.439794),
|
||||
new google.maps.LatLng(37.783371, -122.439687),
|
||||
new google.maps.LatLng(37.783368, -122.439666),
|
||||
new google.maps.LatLng(37.783383, -122.439594),
|
||||
new google.maps.LatLng(37.783508, -122.439525),
|
||||
new google.maps.LatLng(37.783842, -122.439591),
|
||||
new google.maps.LatLng(37.784147, -122.439668),
|
||||
new google.maps.LatLng(37.784206, -122.439686),
|
||||
new google.maps.LatLng(37.784386, -122.43979),
|
||||
new google.maps.LatLng(37.784701, -122.439902),
|
||||
new google.maps.LatLng(37.784965, -122.439938),
|
||||
new google.maps.LatLng(37.78501, -122.439947),
|
||||
new google.maps.LatLng(37.78536, -122.439952),
|
||||
new google.maps.LatLng(37.785715, -122.44003),
|
||||
new google.maps.LatLng(37.786117, -122.440119),
|
||||
new google.maps.LatLng(37.786564, -122.440209),
|
||||
new google.maps.LatLng(37.786905, -122.44027),
|
||||
new google.maps.LatLng(37.786956, -122.440279),
|
||||
new google.maps.LatLng(37.800224, -122.43352),
|
||||
new google.maps.LatLng(37.800155, -122.434101),
|
||||
new google.maps.LatLng(37.80016, -122.43443),
|
||||
new google.maps.LatLng(37.800378, -122.434527),
|
||||
new google.maps.LatLng(37.800738, -122.434598),
|
||||
new google.maps.LatLng(37.800938, -122.43465),
|
||||
new google.maps.LatLng(37.801024, -122.434889),
|
||||
new google.maps.LatLng(37.800955, -122.435392),
|
||||
new google.maps.LatLng(37.800886, -122.435959),
|
||||
new google.maps.LatLng(37.800811, -122.436275),
|
||||
new google.maps.LatLng(37.800788, -122.436299),
|
||||
new google.maps.LatLng(37.800719, -122.436302),
|
||||
new google.maps.LatLng(37.800702, -122.436298),
|
||||
new google.maps.LatLng(37.800661, -122.436273),
|
||||
new google.maps.LatLng(37.800395, -122.436172),
|
||||
new google.maps.LatLng(37.800228, -122.436116),
|
||||
new google.maps.LatLng(37.800169, -122.43613),
|
||||
new google.maps.LatLng(37.800066, -122.436167),
|
||||
new google.maps.LatLng(37.784345, -122.422922),
|
||||
new google.maps.LatLng(37.784389, -122.422926),
|
||||
new google.maps.LatLng(37.784437, -122.422924),
|
||||
new google.maps.LatLng(37.784746, -122.422818),
|
||||
new google.maps.LatLng(37.785436, -122.422959),
|
||||
new google.maps.LatLng(37.78612, -122.423112),
|
||||
new google.maps.LatLng(37.786433, -122.423029),
|
||||
new google.maps.LatLng(37.786631, -122.421213),
|
||||
new google.maps.LatLng(37.78666, -122.421033),
|
||||
new google.maps.LatLng(37.786801, -122.420141),
|
||||
new google.maps.LatLng(37.786823, -122.420034),
|
||||
new google.maps.LatLng(37.786831, -122.419916),
|
||||
new google.maps.LatLng(37.787034, -122.418208),
|
||||
new google.maps.LatLng(37.787056, -122.418034),
|
||||
new google.maps.LatLng(37.787169, -122.417145),
|
||||
new google.maps.LatLng(37.787217, -122.416715),
|
||||
new google.maps.LatLng(37.786144, -122.416403),
|
||||
new google.maps.LatLng(37.785292, -122.416257),
|
||||
new google.maps.LatLng(37.780666, -122.390374),
|
||||
new google.maps.LatLng(37.780501, -122.391281),
|
||||
new google.maps.LatLng(37.780148, -122.392052),
|
||||
new google.maps.LatLng(37.780173, -122.391148),
|
||||
new google.maps.LatLng(37.780693, -122.390592),
|
||||
new google.maps.LatLng(37.781261, -122.391142),
|
||||
new google.maps.LatLng(37.781808, -122.39173),
|
||||
new google.maps.LatLng(37.78234, -122.392341),
|
||||
new google.maps.LatLng(37.782812, -122.393022),
|
||||
new google.maps.LatLng(37.7833, -122.393672),
|
||||
new google.maps.LatLng(37.783809, -122.394275),
|
||||
new google.maps.LatLng(37.784246, -122.394979),
|
||||
new google.maps.LatLng(37.784791, -122.395958),
|
||||
new google.maps.LatLng(37.785675, -122.396746),
|
||||
new google.maps.LatLng(37.786262, -122.39578),
|
||||
new google.maps.LatLng(37.786776, -122.395093),
|
||||
new google.maps.LatLng(37.787282, -122.394426),
|
||||
new google.maps.LatLng(37.787783, -122.393767),
|
||||
new google.maps.LatLng(37.788343, -122.393184),
|
||||
new google.maps.LatLng(37.788895, -122.392506),
|
||||
new google.maps.LatLng(37.789371, -122.391701),
|
||||
new google.maps.LatLng(37.789722, -122.390952),
|
||||
new google.maps.LatLng(37.790315, -122.390305),
|
||||
new google.maps.LatLng(37.790738, -122.389616),
|
||||
new google.maps.LatLng(37.779448, -122.438702),
|
||||
new google.maps.LatLng(37.779023, -122.438585),
|
||||
new google.maps.LatLng(37.778542, -122.438492),
|
||||
new google.maps.LatLng(37.7781, -122.438411),
|
||||
new google.maps.LatLng(37.777986, -122.438376),
|
||||
new google.maps.LatLng(37.77768, -122.438313),
|
||||
new google.maps.LatLng(37.777316, -122.438273),
|
||||
new google.maps.LatLng(37.777135, -122.438254),
|
||||
new google.maps.LatLng(37.776987, -122.438303),
|
||||
new google.maps.LatLng(37.776946, -122.438404),
|
||||
new google.maps.LatLng(37.776944, -122.438467),
|
||||
new google.maps.LatLng(37.776892, -122.438459),
|
||||
new google.maps.LatLng(37.776842, -122.438442),
|
||||
new google.maps.LatLng(37.776822, -122.438391),
|
||||
new google.maps.LatLng(37.776814, -122.438412),
|
||||
new google.maps.LatLng(37.776787, -122.438628),
|
||||
new google.maps.LatLng(37.776729, -122.43865),
|
||||
new google.maps.LatLng(37.776759, -122.438677),
|
||||
new google.maps.LatLng(37.776772, -122.438498),
|
||||
new google.maps.LatLng(37.776787, -122.438389),
|
||||
new google.maps.LatLng(37.776848, -122.438283),
|
||||
new google.maps.LatLng(37.77687, -122.438239),
|
||||
new google.maps.LatLng(37.777015, -122.438198),
|
||||
new google.maps.LatLng(37.777333, -122.438256),
|
||||
new google.maps.LatLng(37.777595, -122.438308),
|
||||
new google.maps.LatLng(37.777797, -122.438344),
|
||||
new google.maps.LatLng(37.77816, -122.438442),
|
||||
new google.maps.LatLng(37.778414, -122.438508),
|
||||
new google.maps.LatLng(37.778445, -122.438516),
|
||||
new google.maps.LatLng(37.778503, -122.438529),
|
||||
new google.maps.LatLng(37.778607, -122.438549),
|
||||
new google.maps.LatLng(37.77867, -122.438644),
|
||||
new google.maps.LatLng(37.778847, -122.438706),
|
||||
new google.maps.LatLng(37.77924, -122.438744),
|
||||
new google.maps.LatLng(37.779738, -122.438822),
|
||||
new google.maps.LatLng(37.780201, -122.438882),
|
||||
new google.maps.LatLng(37.7804, -122.438905),
|
||||
new google.maps.LatLng(37.780501, -122.438921),
|
||||
new google.maps.LatLng(37.780892, -122.438986),
|
||||
new google.maps.LatLng(37.781446, -122.439087),
|
||||
new google.maps.LatLng(37.781985, -122.439199),
|
||||
new google.maps.LatLng(37.782239, -122.439249),
|
||||
new google.maps.LatLng(37.782286, -122.439266),
|
||||
new google.maps.LatLng(37.797847, -122.429388),
|
||||
new google.maps.LatLng(37.797874, -122.42918),
|
||||
new google.maps.LatLng(37.797885, -122.429069),
|
||||
new google.maps.LatLng(37.797887, -122.42905),
|
||||
new google.maps.LatLng(37.797933, -122.428954),
|
||||
new google.maps.LatLng(37.798242, -122.42899),
|
||||
new google.maps.LatLng(37.798617, -122.429075),
|
||||
new google.maps.LatLng(37.798719, -122.429092),
|
||||
new google.maps.LatLng(37.798944, -122.429145),
|
||||
new google.maps.LatLng(37.79932, -122.429251),
|
||||
new google.maps.LatLng(37.79959, -122.429309),
|
||||
new google.maps.LatLng(37.799677, -122.429324),
|
||||
new google.maps.LatLng(37.799966, -122.42936),
|
||||
new google.maps.LatLng(37.800288, -122.42943),
|
||||
new google.maps.LatLng(37.800443, -122.429461),
|
||||
new google.maps.LatLng(37.800465, -122.429474),
|
||||
new google.maps.LatLng(37.800644, -122.42954),
|
||||
new google.maps.LatLng(37.800948, -122.42962),
|
||||
new google.maps.LatLng(37.801242, -122.429685),
|
||||
new google.maps.LatLng(37.801375, -122.429702),
|
||||
new google.maps.LatLng(37.8014, -122.429703),
|
||||
new google.maps.LatLng(37.801453, -122.429707),
|
||||
new google.maps.LatLng(37.801473, -122.429709),
|
||||
new google.maps.LatLng(37.801532, -122.429707),
|
||||
new google.maps.LatLng(37.801852, -122.429729),
|
||||
new google.maps.LatLng(37.802173, -122.429789),
|
||||
new google.maps.LatLng(37.802459, -122.429847),
|
||||
new google.maps.LatLng(37.802554, -122.429825),
|
||||
new google.maps.LatLng(37.802647, -122.429549),
|
||||
new google.maps.LatLng(37.802693, -122.429179),
|
||||
new google.maps.LatLng(37.802729, -122.428751),
|
||||
new google.maps.LatLng(37.766104, -122.409291),
|
||||
new google.maps.LatLng(37.766103, -122.409268),
|
||||
new google.maps.LatLng(37.766138, -122.409229),
|
||||
new google.maps.LatLng(37.766183, -122.409231),
|
||||
new google.maps.LatLng(37.766153, -122.409276),
|
||||
new google.maps.LatLng(37.766005, -122.409365),
|
||||
new google.maps.LatLng(37.765897, -122.40957),
|
||||
new google.maps.LatLng(37.765767, -122.409739),
|
||||
new google.maps.LatLng(37.765693, -122.410389),
|
||||
new google.maps.LatLng(37.765615, -122.411201),
|
||||
new google.maps.LatLng(37.765533, -122.412121),
|
||||
new google.maps.LatLng(37.765467, -122.412939),
|
||||
new google.maps.LatLng(37.765444, -122.414821),
|
||||
new google.maps.LatLng(37.765444, -122.414964),
|
||||
new google.maps.LatLng(37.765318, -122.415424),
|
||||
new google.maps.LatLng(37.763961, -122.415296),
|
||||
new google.maps.LatLng(37.763115, -122.415196),
|
||||
new google.maps.LatLng(37.762967, -122.415183),
|
||||
new google.maps.LatLng(37.762278, -122.415127),
|
||||
new google.maps.LatLng(37.761675, -122.415055),
|
||||
new google.maps.LatLng(37.760932, -122.414988),
|
||||
new google.maps.LatLng(37.759337, -122.414862),
|
||||
new google.maps.LatLng(37.773187, -122.421922),
|
||||
new google.maps.LatLng(37.773043, -122.422118),
|
||||
new google.maps.LatLng(37.773007, -122.422165),
|
||||
new google.maps.LatLng(37.772979, -122.422219),
|
||||
new google.maps.LatLng(37.772865, -122.422394),
|
||||
new google.maps.LatLng(37.772779, -122.422503),
|
||||
new google.maps.LatLng(37.772676, -122.422701),
|
||||
new google.maps.LatLng(37.772606, -122.422806),
|
||||
new google.maps.LatLng(37.772566, -122.42284),
|
||||
new google.maps.LatLng(37.772508, -122.422852),
|
||||
new google.maps.LatLng(37.772387, -122.423011),
|
||||
new google.maps.LatLng(37.772099, -122.423328),
|
||||
new google.maps.LatLng(37.771704, -122.423783),
|
||||
new google.maps.LatLng(37.771481, -122.424081),
|
||||
new google.maps.LatLng(37.7714, -122.424179),
|
||||
new google.maps.LatLng(37.771352, -122.42422),
|
||||
new google.maps.LatLng(37.771248, -122.424327),
|
||||
new google.maps.LatLng(37.770904, -122.424781),
|
||||
new google.maps.LatLng(37.77052, -122.425283),
|
||||
new google.maps.LatLng(37.770337, -122.425553),
|
||||
new google.maps.LatLng(37.770128, -122.425832),
|
||||
new google.maps.LatLng(37.769756, -122.426331),
|
||||
new google.maps.LatLng(37.7693, -122.426902),
|
||||
new google.maps.LatLng(37.769132, -122.427065),
|
||||
new google.maps.LatLng(37.769092, -122.427103),
|
||||
new google.maps.LatLng(37.768979, -122.427172),
|
||||
new google.maps.LatLng(37.768595, -122.427634),
|
||||
new google.maps.LatLng(37.768372, -122.427913),
|
||||
new google.maps.LatLng(37.768337, -122.427961),
|
||||
new google.maps.LatLng(37.768244, -122.428138),
|
||||
new google.maps.LatLng(37.767942, -122.428581),
|
||||
new google.maps.LatLng(37.767482, -122.429094),
|
||||
new google.maps.LatLng(37.767031, -122.429606),
|
||||
new google.maps.LatLng(37.766732, -122.429986),
|
||||
new google.maps.LatLng(37.76668, -122.430058),
|
||||
new google.maps.LatLng(37.766633, -122.430109),
|
||||
new google.maps.LatLng(37.76658, -122.430211),
|
||||
new google.maps.LatLng(37.766367, -122.430594),
|
||||
new google.maps.LatLng(37.76591, -122.431137),
|
||||
new google.maps.LatLng(37.765353, -122.431806),
|
||||
new google.maps.LatLng(37.764962, -122.432298),
|
||||
new google.maps.LatLng(37.764868, -122.432486),
|
||||
new google.maps.LatLng(37.764518, -122.432913),
|
||||
new google.maps.LatLng(37.763435, -122.434173),
|
||||
new google.maps.LatLng(37.762847, -122.434953),
|
||||
new google.maps.LatLng(37.762291, -122.435935),
|
||||
new google.maps.LatLng(37.762224, -122.436074),
|
||||
new google.maps.LatLng(37.761957, -122.436892),
|
||||
new google.maps.LatLng(37.761652, -122.438886),
|
||||
new google.maps.LatLng(37.761284, -122.439955),
|
||||
new google.maps.LatLng(37.76121, -122.440068),
|
||||
new google.maps.LatLng(37.761064, -122.44072),
|
||||
new google.maps.LatLng(37.76104, -122.441411),
|
||||
new google.maps.LatLng(37.761048, -122.442324),
|
||||
new google.maps.LatLng(37.760851, -122.443118),
|
||||
new google.maps.LatLng(37.759977, -122.444591),
|
||||
new google.maps.LatLng(37.759913, -122.444698),
|
||||
new google.maps.LatLng(37.759623, -122.445065),
|
||||
new google.maps.LatLng(37.758902, -122.445158),
|
||||
new google.maps.LatLng(37.758428, -122.44457),
|
||||
new google.maps.LatLng(37.757687, -122.44334),
|
||||
new google.maps.LatLng(37.757583, -122.44324),
|
||||
new google.maps.LatLng(37.757019, -122.442787),
|
||||
new google.maps.LatLng(37.756603, -122.442322),
|
||||
new google.maps.LatLng(37.75638, -122.441602),
|
||||
new google.maps.LatLng(37.75579, -122.441382),
|
||||
new google.maps.LatLng(37.754493, -122.442133),
|
||||
new google.maps.LatLng(37.754361, -122.442206),
|
||||
new google.maps.LatLng(37.753719, -122.44265),
|
||||
new google.maps.LatLng(37.753096, -122.442915),
|
||||
new google.maps.LatLng(37.751617, -122.443211),
|
||||
new google.maps.LatLng(37.751496, -122.443246),
|
||||
new google.maps.LatLng(37.750733, -122.443428),
|
||||
new google.maps.LatLng(37.750126, -122.443536),
|
||||
new google.maps.LatLng(37.750103, -122.443784),
|
||||
new google.maps.LatLng(37.75039, -122.44401),
|
||||
new google.maps.LatLng(37.750448, -122.444013),
|
||||
new google.maps.LatLng(37.750536, -122.44404),
|
||||
new google.maps.LatLng(37.750493, -122.444141),
|
||||
new google.maps.LatLng(37.790859, -122.402808),
|
||||
new google.maps.LatLng(37.790864, -122.402768),
|
||||
new google.maps.LatLng(37.790995, -122.402539),
|
||||
new google.maps.LatLng(37.791148, -122.402172),
|
||||
new google.maps.LatLng(37.791385, -122.401312),
|
||||
new google.maps.LatLng(37.791405, -122.400776),
|
||||
new google.maps.LatLng(37.791288, -122.400528),
|
||||
new google.maps.LatLng(37.791113, -122.400441),
|
||||
new google.maps.LatLng(37.791027, -122.400395),
|
||||
new google.maps.LatLng(37.791094, -122.400311),
|
||||
new google.maps.LatLng(37.791211, -122.400183),
|
||||
new google.maps.LatLng(37.79106, -122.399334),
|
||||
new google.maps.LatLng(37.790538, -122.398718),
|
||||
new google.maps.LatLng(37.790095, -122.398086),
|
||||
new google.maps.LatLng(37.789644, -122.39736),
|
||||
new google.maps.LatLng(37.789254, -122.396844),
|
||||
new google.maps.LatLng(37.788855, -122.396397),
|
||||
new google.maps.LatLng(37.788483, -122.395963),
|
||||
new google.maps.LatLng(37.788015, -122.395365),
|
||||
new google.maps.LatLng(37.787558, -122.394735),
|
||||
new google.maps.LatLng(37.787472, -122.394323),
|
||||
new google.maps.LatLng(37.78763, -122.394025),
|
||||
new google.maps.LatLng(37.787767, -122.393987),
|
||||
new google.maps.LatLng(37.787486, -122.394452),
|
||||
new google.maps.LatLng(37.786977, -122.395043),
|
||||
new google.maps.LatLng(37.786583, -122.395552),
|
||||
new google.maps.LatLng(37.78654, -122.39561),
|
||||
new google.maps.LatLng(37.786516, -122.395659),
|
||||
new google.maps.LatLng(37.786378, -122.395707),
|
||||
new google.maps.LatLng(37.786044, -122.395362),
|
||||
new google.maps.LatLng(37.785598, -122.394715),
|
||||
new google.maps.LatLng(37.785321, -122.394361),
|
||||
new google.maps.LatLng(37.785207, -122.394236),
|
||||
new google.maps.LatLng(37.785751, -122.394062),
|
||||
new google.maps.LatLng(37.785996, -122.393881),
|
||||
new google.maps.LatLng(37.786092, -122.39383),
|
||||
new google.maps.LatLng(37.785998, -122.393899),
|
||||
new google.maps.LatLng(37.785114, -122.394365),
|
||||
new google.maps.LatLng(37.785022, -122.394441),
|
||||
new google.maps.LatLng(37.784823, -122.394635),
|
||||
new google.maps.LatLng(37.784719, -122.394629),
|
||||
new google.maps.LatLng(37.785069, -122.394176),
|
||||
new google.maps.LatLng(37.7855, -122.39365),
|
||||
new google.maps.LatLng(37.78577, -122.393291),
|
||||
new google.maps.LatLng(37.785839, -122.393159),
|
||||
new google.maps.LatLng(37.782651, -122.400628),
|
||||
new google.maps.LatLng(37.782616, -122.400599),
|
||||
new google.maps.LatLng(37.782702, -122.40047),
|
||||
new google.maps.LatLng(37.782915, -122.400192),
|
||||
new google.maps.LatLng(37.783137, -122.399887),
|
||||
new google.maps.LatLng(37.783414, -122.399519),
|
||||
new google.maps.LatLng(37.783629, -122.399237),
|
||||
new google.maps.LatLng(37.783688, -122.399157),
|
||||
new google.maps.LatLng(37.783716, -122.399106),
|
||||
new google.maps.LatLng(37.783798, -122.399072),
|
||||
new google.maps.LatLng(37.783997, -122.399186),
|
||||
new google.maps.LatLng(37.784271, -122.399538),
|
||||
new google.maps.LatLng(37.784577, -122.399948),
|
||||
new google.maps.LatLng(37.784828, -122.40026),
|
||||
new google.maps.LatLng(37.784999, -122.400477),
|
||||
new google.maps.LatLng(37.785113, -122.400651),
|
||||
new google.maps.LatLng(37.785155, -122.400703),
|
||||
new google.maps.LatLng(37.785192, -122.400749),
|
||||
new google.maps.LatLng(37.785278, -122.400839),
|
||||
new google.maps.LatLng(37.785387, -122.400857),
|
||||
new google.maps.LatLng(37.785478, -122.40089),
|
||||
new google.maps.LatLng(37.785526, -122.401022),
|
||||
new google.maps.LatLng(37.785598, -122.401148),
|
||||
new google.maps.LatLng(37.785631, -122.401202),
|
||||
new google.maps.LatLng(37.78566, -122.401267),
|
||||
new google.maps.LatLng(37.803986, -122.426035),
|
||||
new google.maps.LatLng(37.804102, -122.425089),
|
||||
new google.maps.LatLng(37.804211, -122.424156),
|
||||
new google.maps.LatLng(37.803861, -122.423385),
|
||||
new google.maps.LatLng(37.803151, -122.423214),
|
||||
new google.maps.LatLng(37.802439, -122.423077),
|
||||
new google.maps.LatLng(37.80174, -122.422905),
|
||||
new google.maps.LatLng(37.801069, -122.422785),
|
||||
new google.maps.LatLng(37.800345, -122.422649),
|
||||
new google.maps.LatLng(37.799633, -122.422603),
|
||||
new google.maps.LatLng(37.79975, -122.4217),
|
||||
new google.maps.LatLng(37.799885, -122.420854),
|
||||
new google.maps.LatLng(37.799209, -122.420607),
|
||||
new google.maps.LatLng(37.795656, -122.400395),
|
||||
new google.maps.LatLng(37.795203, -122.400304),
|
||||
new google.maps.LatLng(37.778738, -122.415584),
|
||||
new google.maps.LatLng(37.778812, -122.415189),
|
||||
new google.maps.LatLng(37.778824, -122.415092),
|
||||
new google.maps.LatLng(37.778833, -122.414932),
|
||||
new google.maps.LatLng(37.778834, -122.414898),
|
||||
new google.maps.LatLng(37.77874, -122.414757),
|
||||
new google.maps.LatLng(37.778501, -122.414433),
|
||||
new google.maps.LatLng(37.778182, -122.414026),
|
||||
new google.maps.LatLng(37.777851, -122.413623),
|
||||
new google.maps.LatLng(37.777486, -122.413166),
|
||||
new google.maps.LatLng(37.777109, -122.412674),
|
||||
new google.maps.LatLng(37.776743, -122.412186),
|
||||
new google.maps.LatLng(37.77644, -122.4118),
|
||||
new google.maps.LatLng(37.776295, -122.411614),
|
||||
new google.maps.LatLng(37.776158, -122.41144),
|
||||
new google.maps.LatLng(37.775806, -122.410997),
|
||||
new google.maps.LatLng(37.775422, -122.410484),
|
||||
new google.maps.LatLng(37.775126, -122.410087),
|
||||
new google.maps.LatLng(37.775012, -122.409854),
|
||||
new google.maps.LatLng(37.775164, -122.409573),
|
||||
new google.maps.LatLng(37.775498, -122.40918),
|
||||
new google.maps.LatLng(37.775868, -122.40873),
|
||||
new google.maps.LatLng(37.776256, -122.40824),
|
||||
new google.maps.LatLng(37.776519, -122.407928),
|
||||
new google.maps.LatLng(37.776539, -122.407904),
|
||||
new google.maps.LatLng(37.776595, -122.407854),
|
||||
new google.maps.LatLng(37.776853, -122.407547),
|
||||
new google.maps.LatLng(37.777234, -122.407087),
|
||||
new google.maps.LatLng(37.777644, -122.406558),
|
||||
new google.maps.LatLng(37.778066, -122.406017),
|
||||
new google.maps.LatLng(37.778468, -122.405499),
|
||||
new google.maps.LatLng(37.778866, -122.404995),
|
||||
new google.maps.LatLng(37.779295, -122.404455),
|
||||
new google.maps.LatLng(37.779695, -122.40395),
|
||||
new google.maps.LatLng(37.779982, -122.403584),
|
||||
new google.maps.LatLng(37.780295, -122.403223),
|
||||
new google.maps.LatLng(37.780664, -122.402766),
|
||||
new google.maps.LatLng(37.781043, -122.402288),
|
||||
new google.maps.LatLng(37.781399, -122.401823),
|
||||
new google.maps.LatLng(37.781727, -122.401407),
|
||||
new google.maps.LatLng(37.781853, -122.401247),
|
||||
new google.maps.LatLng(37.781894, -122.401195),
|
||||
new google.maps.LatLng(37.782076, -122.400977),
|
||||
new google.maps.LatLng(37.782338, -122.400603),
|
||||
new google.maps.LatLng(37.782666, -122.400133),
|
||||
new google.maps.LatLng(37.783048, -122.399634),
|
||||
new google.maps.LatLng(37.78345, -122.399198),
|
||||
new google.maps.LatLng(37.783791, -122.398998),
|
||||
new google.maps.LatLng(37.784177, -122.398959),
|
||||
new google.maps.LatLng(37.784388, -122.398971),
|
||||
new google.maps.LatLng(37.784404, -122.399128),
|
||||
new google.maps.LatLng(37.784586, -122.399524),
|
||||
new google.maps.LatLng(37.784835, -122.399927),
|
||||
new google.maps.LatLng(37.785116, -122.400307),
|
||||
new google.maps.LatLng(37.785282, -122.400539),
|
||||
new google.maps.LatLng(37.785346, -122.400692),
|
||||
new google.maps.LatLng(37.765769, -122.407201),
|
||||
new google.maps.LatLng(37.76579, -122.407414),
|
||||
new google.maps.LatLng(37.765802, -122.407755),
|
||||
new google.maps.LatLng(37.765791, -122.408219),
|
||||
new google.maps.LatLng(37.765763, -122.408759),
|
||||
new google.maps.LatLng(37.765726, -122.409348),
|
||||
new google.maps.LatLng(37.765716, -122.409882),
|
||||
new google.maps.LatLng(37.765708, -122.410202),
|
||||
new google.maps.LatLng(37.765705, -122.410253),
|
||||
new google.maps.LatLng(37.765707, -122.410369),
|
||||
new google.maps.LatLng(37.765692, -122.41072),
|
||||
new google.maps.LatLng(37.765699, -122.411215),
|
||||
new google.maps.LatLng(37.765687, -122.411789),
|
||||
new google.maps.LatLng(37.765666, -122.412373),
|
||||
new google.maps.LatLng(37.765598, -122.412883),
|
||||
new google.maps.LatLng(37.765543, -122.413039),
|
||||
new google.maps.LatLng(37.765532, -122.413125),
|
||||
new google.maps.LatLng(37.7655, -122.413553),
|
||||
new google.maps.LatLng(37.765448, -122.414053),
|
||||
new google.maps.LatLng(37.765388, -122.414645),
|
||||
new google.maps.LatLng(37.765323, -122.41525),
|
||||
new google.maps.LatLng(37.765303, -122.415847),
|
||||
new google.maps.LatLng(37.765251, -122.416439),
|
||||
new google.maps.LatLng(37.765204, -122.41702),
|
||||
new google.maps.LatLng(37.765172, -122.417556),
|
||||
new google.maps.LatLng(37.765164, -122.418075),
|
||||
new google.maps.LatLng(37.765153, -122.418618),
|
||||
new google.maps.LatLng(37.765136, -122.419112),
|
||||
new google.maps.LatLng(37.765129, -122.419378),
|
||||
new google.maps.LatLng(37.765119, -122.419481),
|
||||
new google.maps.LatLng(37.7651, -122.419852),
|
||||
new google.maps.LatLng(37.765083, -122.420349),
|
||||
new google.maps.LatLng(37.765045, -122.42093),
|
||||
new google.maps.LatLng(37.764992, -122.421481),
|
||||
new google.maps.LatLng(37.76498, -122.421695),
|
||||
new google.maps.LatLng(37.764993, -122.421843),
|
||||
new google.maps.LatLng(37.764986, -122.422255),
|
||||
new google.maps.LatLng(37.764975, -122.422823),
|
||||
new google.maps.LatLng(37.764939, -122.423411),
|
||||
new google.maps.LatLng(37.764902, -122.424014),
|
||||
new google.maps.LatLng(37.764853, -122.424576),
|
||||
new google.maps.LatLng(37.764826, -122.424922),
|
||||
new google.maps.LatLng(37.764796, -122.425375),
|
||||
new google.maps.LatLng(37.764782, -122.425869),
|
||||
new google.maps.LatLng(37.764768, -122.426089),
|
||||
new google.maps.LatLng(37.764766, -122.426117),
|
||||
new google.maps.LatLng(37.764723, -122.426276),
|
||||
new google.maps.LatLng(37.764681, -122.426649),
|
||||
new google.maps.LatLng(37.782012, -122.4042),
|
||||
new google.maps.LatLng(37.781574, -122.404911),
|
||||
new google.maps.LatLng(37.781055, -122.405597),
|
||||
new google.maps.LatLng(37.780479, -122.406341),
|
||||
new google.maps.LatLng(37.779996, -122.406939),
|
||||
new google.maps.LatLng(37.779459, -122.407613),
|
||||
new google.maps.LatLng(37.778953, -122.408228),
|
||||
new google.maps.LatLng(37.778409, -122.408839),
|
||||
new google.maps.LatLng(37.777842, -122.409501),
|
||||
new google.maps.LatLng(37.777334, -122.410181),
|
||||
new google.maps.LatLng(37.776809, -122.410836),
|
||||
new google.maps.LatLng(37.77624, -122.411514),
|
||||
new google.maps.LatLng(37.775725, -122.412145),
|
||||
new google.maps.LatLng(37.77519, -122.412805),
|
||||
new google.maps.LatLng(37.774672, -122.413464),
|
||||
new google.maps.LatLng(37.774084, -122.414186),
|
||||
new google.maps.LatLng(37.773533, -122.413636),
|
||||
new google.maps.LatLng(37.773021, -122.413009),
|
||||
new google.maps.LatLng(37.772501, -122.412371),
|
||||
new google.maps.LatLng(37.771964, -122.411681),
|
||||
new google.maps.LatLng(37.771479, -122.411078),
|
||||
new google.maps.LatLng(37.770992, -122.410477),
|
||||
new google.maps.LatLng(37.770467, -122.409801),
|
||||
new google.maps.LatLng(37.77009, -122.408904),
|
||||
new google.maps.LatLng(37.769657, -122.408103),
|
||||
new google.maps.LatLng(37.769132, -122.407276),
|
||||
new google.maps.LatLng(37.768564, -122.406469),
|
||||
new google.maps.LatLng(37.76798, -122.405745),
|
||||
new google.maps.LatLng(37.76738, -122.405299),
|
||||
new google.maps.LatLng(37.766604, -122.405297),
|
||||
new google.maps.LatLng(37.765838, -122.4052),
|
||||
new google.maps.LatLng(37.765139, -122.405139),
|
||||
new google.maps.LatLng(37.764457, -122.405094),
|
||||
new google.maps.LatLng(37.763716, -122.405142),
|
||||
new google.maps.LatLng(37.762932, -122.405398),
|
||||
new google.maps.LatLng(37.762126, -122.405813),
|
||||
new google.maps.LatLng(37.761344, -122.406215),
|
||||
new google.maps.LatLng(37.760556, -122.406495),
|
||||
new google.maps.LatLng(37.759732, -122.406484),
|
||||
new google.maps.LatLng(37.75891, -122.406228),
|
||||
new google.maps.LatLng(37.758182, -122.405695),
|
||||
new google.maps.LatLng(37.757676, -122.405118),
|
||||
new google.maps.LatLng(37.757039, -122.404346),
|
||||
new google.maps.LatLng(37.756335, -122.403719),
|
||||
new google.maps.LatLng(37.755503, -122.403406),
|
||||
new google.maps.LatLng(37.754665, -122.403242),
|
||||
new google.maps.LatLng(37.753837, -122.403172),
|
||||
new google.maps.LatLng(37.752986, -122.403112),
|
||||
new google.maps.LatLng(37.751266, -122.403355),
|
||||
];
|
||||
}
|
||||
// [END maps_layer_heatmap]
|
||||
export { initMap };
|
@ -1,21 +0,0 @@
|
||||
import { Map, GoogleApiWrapper } from 'google-maps-react';
|
||||
|
||||
export function Heatmap() {
|
||||
const mapStyles = {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
};
|
||||
|
||||
return (
|
||||
<Map
|
||||
google={this.props.google}
|
||||
zoom={8}
|
||||
style={mapStyles}
|
||||
initialCenter={{ lat: 47.444, lng: -122.176 }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default GoogleApiWrapper({
|
||||
apiKey: 'AIzaSyCZ51VFfxqZ2GkCmVrcNZdUKsM0fuBQUCY'
|
||||
});
|
208
Birdmap.API/ClientApp/src/components/logs/LogService.js
Normal file
208
Birdmap.API/ClientApp/src/components/logs/LogService.js
Normal file
@ -0,0 +1,208 @@
|
||||
"use strict";
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
//----------------------
|
||||
// <auto-generated>
|
||||
// Generated using the NSwag toolchain v13.8.2.0 (NJsonSchema v10.2.1.0 (Newtonsoft.Json v12.0.0.0)) (http://NSwag.org)
|
||||
// </auto-generated>
|
||||
//----------------------
|
||||
// ReSharper disable InconsistentNaming
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = function (d, b) {
|
||||
extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
||||
return extendStatics(d, b);
|
||||
};
|
||||
return function (d, b) {
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ApiException = exports.HttpStatusCode = void 0;
|
||||
var LogService = /** @class */ (function () {
|
||||
function LogService(baseUrl, http) {
|
||||
this.jsonParseReviver = undefined;
|
||||
this.http = http ? http : window;
|
||||
this.baseUrl = baseUrl !== undefined && baseUrl !== null ? baseUrl : "api/logs";
|
||||
}
|
||||
LogService.prototype.getAll = function () {
|
||||
var _this = this;
|
||||
var url_ = this.baseUrl + "/all";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
var options_ = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
return this.http.fetch(url_, options_).then(function (_response) {
|
||||
return _this.processGetAll(_response);
|
||||
});
|
||||
};
|
||||
LogService.prototype.processGetAll = function (response) {
|
||||
var _this = this;
|
||||
var status = response.status;
|
||||
var _headers = {};
|
||||
if (response.headers && response.headers.forEach) {
|
||||
response.headers.forEach(function (v, k) { return _headers[k] = v; });
|
||||
}
|
||||
;
|
||||
if (status === 200) {
|
||||
return response.text().then(function (_responseText) {
|
||||
var result200 = null;
|
||||
var resultData200 = _responseText === "" ? null : JSON.parse(_responseText, _this.jsonParseReviver);
|
||||
if (Array.isArray(resultData200)) {
|
||||
result200 = [];
|
||||
for (var _i = 0, resultData200_1 = resultData200; _i < resultData200_1.length; _i++) {
|
||||
var item = resultData200_1[_i];
|
||||
result200.push(item);
|
||||
}
|
||||
}
|
||||
return result200;
|
||||
});
|
||||
}
|
||||
else if (status !== 200 && status !== 204) {
|
||||
return response.text().then(function (_responseText) {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
};
|
||||
LogService.prototype.getFiles = function (filenames) {
|
||||
var _this = this;
|
||||
var url_ = this.baseUrl + "?";
|
||||
if (filenames !== undefined && filenames !== null)
|
||||
filenames && filenames.forEach(function (item) { url_ += "filenames=" + encodeURIComponent("" + item) + "&"; });
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
var options_ = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/octet-stream",
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
return this.http.fetch(url_, options_).then(function (_response) {
|
||||
return _this.processGetFiles(_response);
|
||||
});
|
||||
};
|
||||
LogService.prototype.processGetFiles = function (response) {
|
||||
var status = response.status;
|
||||
var _headers = {};
|
||||
if (response.headers && response.headers.forEach) {
|
||||
response.headers.forEach(function (v, k) { return _headers[k] = v; });
|
||||
}
|
||||
;
|
||||
if (status === 200 || status === 206) {
|
||||
var contentDisposition = response.headers ? response.headers.get("content-disposition") : undefined;
|
||||
var fileNameMatch = contentDisposition ? /filename="?([^"]*?)"?(;|$)/g.exec(contentDisposition) : undefined;
|
||||
var fileName_1 = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[1] : undefined;
|
||||
return response.blob().then(function (blob) { return { fileName: fileName_1, data: blob, status: status, headers: _headers }; });
|
||||
}
|
||||
else if (status !== 200 && status !== 204) {
|
||||
return response.text().then(function (_responseText) {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
};
|
||||
return LogService;
|
||||
}());
|
||||
exports.default = LogService;
|
||||
var HttpStatusCode;
|
||||
(function (HttpStatusCode) {
|
||||
HttpStatusCode["Continue"] = "Continue";
|
||||
HttpStatusCode["SwitchingProtocols"] = "SwitchingProtocols";
|
||||
HttpStatusCode["Processing"] = "Processing";
|
||||
HttpStatusCode["EarlyHints"] = "EarlyHints";
|
||||
HttpStatusCode["OK"] = "OK";
|
||||
HttpStatusCode["Created"] = "Created";
|
||||
HttpStatusCode["Accepted"] = "Accepted";
|
||||
HttpStatusCode["NonAuthoritativeInformation"] = "NonAuthoritativeInformation";
|
||||
HttpStatusCode["NoContent"] = "NoContent";
|
||||
HttpStatusCode["ResetContent"] = "ResetContent";
|
||||
HttpStatusCode["PartialContent"] = "PartialContent";
|
||||
HttpStatusCode["MultiStatus"] = "MultiStatus";
|
||||
HttpStatusCode["AlreadyReported"] = "AlreadyReported";
|
||||
HttpStatusCode["IMUsed"] = "IMUsed";
|
||||
HttpStatusCode["MultipleChoices"] = "Ambiguous";
|
||||
HttpStatusCode["Ambiguous"] = "Ambiguous";
|
||||
HttpStatusCode["MovedPermanently"] = "Moved";
|
||||
HttpStatusCode["Moved"] = "Moved";
|
||||
HttpStatusCode["Found"] = "Redirect";
|
||||
HttpStatusCode["Redirect"] = "Redirect";
|
||||
HttpStatusCode["SeeOther"] = "RedirectMethod";
|
||||
HttpStatusCode["RedirectMethod"] = "RedirectMethod";
|
||||
HttpStatusCode["NotModified"] = "NotModified";
|
||||
HttpStatusCode["UseProxy"] = "UseProxy";
|
||||
HttpStatusCode["Unused"] = "Unused";
|
||||
HttpStatusCode["TemporaryRedirect"] = "TemporaryRedirect";
|
||||
HttpStatusCode["RedirectKeepVerb"] = "TemporaryRedirect";
|
||||
HttpStatusCode["PermanentRedirect"] = "PermanentRedirect";
|
||||
HttpStatusCode["BadRequest"] = "BadRequest";
|
||||
HttpStatusCode["Unauthorized"] = "Unauthorized";
|
||||
HttpStatusCode["PaymentRequired"] = "PaymentRequired";
|
||||
HttpStatusCode["Forbidden"] = "Forbidden";
|
||||
HttpStatusCode["NotFound"] = "NotFound";
|
||||
HttpStatusCode["MethodNotAllowed"] = "MethodNotAllowed";
|
||||
HttpStatusCode["NotAcceptable"] = "NotAcceptable";
|
||||
HttpStatusCode["ProxyAuthenticationRequired"] = "ProxyAuthenticationRequired";
|
||||
HttpStatusCode["RequestTimeout"] = "RequestTimeout";
|
||||
HttpStatusCode["Conflict"] = "Conflict";
|
||||
HttpStatusCode["Gone"] = "Gone";
|
||||
HttpStatusCode["LengthRequired"] = "LengthRequired";
|
||||
HttpStatusCode["PreconditionFailed"] = "PreconditionFailed";
|
||||
HttpStatusCode["RequestEntityTooLarge"] = "RequestEntityTooLarge";
|
||||
HttpStatusCode["RequestUriTooLong"] = "RequestUriTooLong";
|
||||
HttpStatusCode["UnsupportedMediaType"] = "UnsupportedMediaType";
|
||||
HttpStatusCode["RequestedRangeNotSatisfiable"] = "RequestedRangeNotSatisfiable";
|
||||
HttpStatusCode["ExpectationFailed"] = "ExpectationFailed";
|
||||
HttpStatusCode["MisdirectedRequest"] = "MisdirectedRequest";
|
||||
HttpStatusCode["UnprocessableEntity"] = "UnprocessableEntity";
|
||||
HttpStatusCode["Locked"] = "Locked";
|
||||
HttpStatusCode["FailedDependency"] = "FailedDependency";
|
||||
HttpStatusCode["UpgradeRequired"] = "UpgradeRequired";
|
||||
HttpStatusCode["PreconditionRequired"] = "PreconditionRequired";
|
||||
HttpStatusCode["TooManyRequests"] = "TooManyRequests";
|
||||
HttpStatusCode["RequestHeaderFieldsTooLarge"] = "RequestHeaderFieldsTooLarge";
|
||||
HttpStatusCode["UnavailableForLegalReasons"] = "UnavailableForLegalReasons";
|
||||
HttpStatusCode["InternalServerError"] = "InternalServerError";
|
||||
HttpStatusCode["NotImplemented"] = "NotImplemented";
|
||||
HttpStatusCode["BadGateway"] = "BadGateway";
|
||||
HttpStatusCode["ServiceUnavailable"] = "ServiceUnavailable";
|
||||
HttpStatusCode["GatewayTimeout"] = "GatewayTimeout";
|
||||
HttpStatusCode["HttpVersionNotSupported"] = "HttpVersionNotSupported";
|
||||
HttpStatusCode["VariantAlsoNegotiates"] = "VariantAlsoNegotiates";
|
||||
HttpStatusCode["InsufficientStorage"] = "InsufficientStorage";
|
||||
HttpStatusCode["LoopDetected"] = "LoopDetected";
|
||||
HttpStatusCode["NotExtended"] = "NotExtended";
|
||||
HttpStatusCode["NetworkAuthenticationRequired"] = "NetworkAuthenticationRequired";
|
||||
})(HttpStatusCode = exports.HttpStatusCode || (exports.HttpStatusCode = {}));
|
||||
var ApiException = /** @class */ (function (_super) {
|
||||
__extends(ApiException, _super);
|
||||
function ApiException(message, status, response, headers, result) {
|
||||
var _this = _super.call(this) || this;
|
||||
_this.isApiException = true;
|
||||
_this.message = message;
|
||||
_this.status = status;
|
||||
_this.response = response;
|
||||
_this.headers = headers;
|
||||
_this.result = result;
|
||||
return _this;
|
||||
}
|
||||
ApiException.isApiException = function (obj) {
|
||||
return obj.isApiException === true;
|
||||
};
|
||||
return ApiException;
|
||||
}(Error));
|
||||
exports.ApiException = ApiException;
|
||||
function throwException(message, status, response, headers, result) {
|
||||
if (result !== null && result !== undefined)
|
||||
throw result;
|
||||
else
|
||||
throw new ApiException(message, status, response, headers, null);
|
||||
}
|
||||
//# sourceMappingURL=LogService.js.map
|
File diff suppressed because one or more lines are too long
201
Birdmap.API/ClientApp/src/components/logs/LogService.ts
Normal file
201
Birdmap.API/ClientApp/src/components/logs/LogService.ts
Normal file
@ -0,0 +1,201 @@
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
//----------------------
|
||||
// <auto-generated>
|
||||
// Generated using the NSwag toolchain v13.8.2.0 (NJsonSchema v10.2.1.0 (Newtonsoft.Json v12.0.0.0)) (http://NSwag.org)
|
||||
// </auto-generated>
|
||||
//----------------------
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
export default class LogService {
|
||||
private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
|
||||
private baseUrl: string;
|
||||
protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
|
||||
|
||||
constructor(baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> }) {
|
||||
this.http = http ? http : <any>window;
|
||||
this.baseUrl = baseUrl !== undefined && baseUrl !== null ? baseUrl : "api/logs";
|
||||
}
|
||||
|
||||
getAll(): Promise<string[]> {
|
||||
let url_ = this.baseUrl + "/all";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_ = <RequestInit>{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processGetAll(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processGetAll(response: Response): Promise<string[]> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 200) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result200: any = null;
|
||||
let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
if (Array.isArray(resultData200)) {
|
||||
result200 = [] as any;
|
||||
for (let item of resultData200)
|
||||
result200!.push(item);
|
||||
}
|
||||
return result200;
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<string[]>(<any>null);
|
||||
}
|
||||
|
||||
getFiles(filenames: string[] | null | undefined): Promise<FileResponse | null> {
|
||||
let url_ = this.baseUrl + "?";
|
||||
if (filenames !== undefined && filenames !== null)
|
||||
filenames && filenames.forEach(item => { url_ += "filenames=" + encodeURIComponent("" + item) + "&"; });
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_ = <RequestInit>{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/octet-stream",
|
||||
'Authorization': sessionStorage.getItem('user')
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processGetFiles(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processGetFiles(response: Response): Promise<FileResponse | null> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 200 || status === 206) {
|
||||
const contentDisposition = response.headers ? response.headers.get("content-disposition") : undefined;
|
||||
const fileNameMatch = contentDisposition ? /filename="?([^"]*?)"?(;|$)/g.exec(contentDisposition) : undefined;
|
||||
const fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[1] : undefined;
|
||||
return response.blob().then(blob => { return { fileName: fileName, data: blob, status: status, headers: _headers }; });
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<FileResponse | null>(<any>null);
|
||||
}
|
||||
}
|
||||
|
||||
export interface FileResponse {
|
||||
data: Blob;
|
||||
status: number;
|
||||
fileName?: string;
|
||||
headers?: { [name: string]: any };
|
||||
}
|
||||
|
||||
export enum HttpStatusCode {
|
||||
Continue = "Continue",
|
||||
SwitchingProtocols = "SwitchingProtocols",
|
||||
Processing = "Processing",
|
||||
EarlyHints = "EarlyHints",
|
||||
OK = "OK",
|
||||
Created = "Created",
|
||||
Accepted = "Accepted",
|
||||
NonAuthoritativeInformation = "NonAuthoritativeInformation",
|
||||
NoContent = "NoContent",
|
||||
ResetContent = "ResetContent",
|
||||
PartialContent = "PartialContent",
|
||||
MultiStatus = "MultiStatus",
|
||||
AlreadyReported = "AlreadyReported",
|
||||
IMUsed = "IMUsed",
|
||||
MultipleChoices = "Ambiguous",
|
||||
Ambiguous = "Ambiguous",
|
||||
MovedPermanently = "Moved",
|
||||
Moved = "Moved",
|
||||
Found = "Redirect",
|
||||
Redirect = "Redirect",
|
||||
SeeOther = "RedirectMethod",
|
||||
RedirectMethod = "RedirectMethod",
|
||||
NotModified = "NotModified",
|
||||
UseProxy = "UseProxy",
|
||||
Unused = "Unused",
|
||||
TemporaryRedirect = "TemporaryRedirect",
|
||||
RedirectKeepVerb = "TemporaryRedirect",
|
||||
PermanentRedirect = "PermanentRedirect",
|
||||
BadRequest = "BadRequest",
|
||||
Unauthorized = "Unauthorized",
|
||||
PaymentRequired = "PaymentRequired",
|
||||
Forbidden = "Forbidden",
|
||||
NotFound = "NotFound",
|
||||
MethodNotAllowed = "MethodNotAllowed",
|
||||
NotAcceptable = "NotAcceptable",
|
||||
ProxyAuthenticationRequired = "ProxyAuthenticationRequired",
|
||||
RequestTimeout = "RequestTimeout",
|
||||
Conflict = "Conflict",
|
||||
Gone = "Gone",
|
||||
LengthRequired = "LengthRequired",
|
||||
PreconditionFailed = "PreconditionFailed",
|
||||
RequestEntityTooLarge = "RequestEntityTooLarge",
|
||||
RequestUriTooLong = "RequestUriTooLong",
|
||||
UnsupportedMediaType = "UnsupportedMediaType",
|
||||
RequestedRangeNotSatisfiable = "RequestedRangeNotSatisfiable",
|
||||
ExpectationFailed = "ExpectationFailed",
|
||||
MisdirectedRequest = "MisdirectedRequest",
|
||||
UnprocessableEntity = "UnprocessableEntity",
|
||||
Locked = "Locked",
|
||||
FailedDependency = "FailedDependency",
|
||||
UpgradeRequired = "UpgradeRequired",
|
||||
PreconditionRequired = "PreconditionRequired",
|
||||
TooManyRequests = "TooManyRequests",
|
||||
RequestHeaderFieldsTooLarge = "RequestHeaderFieldsTooLarge",
|
||||
UnavailableForLegalReasons = "UnavailableForLegalReasons",
|
||||
InternalServerError = "InternalServerError",
|
||||
NotImplemented = "NotImplemented",
|
||||
BadGateway = "BadGateway",
|
||||
ServiceUnavailable = "ServiceUnavailable",
|
||||
GatewayTimeout = "GatewayTimeout",
|
||||
HttpVersionNotSupported = "HttpVersionNotSupported",
|
||||
VariantAlsoNegotiates = "VariantAlsoNegotiates",
|
||||
InsufficientStorage = "InsufficientStorage",
|
||||
LoopDetected = "LoopDetected",
|
||||
NotExtended = "NotExtended",
|
||||
NetworkAuthenticationRequired = "NetworkAuthenticationRequired",
|
||||
}
|
||||
|
||||
export class ApiException extends Error {
|
||||
message: string;
|
||||
status: number;
|
||||
response: string;
|
||||
headers: { [key: string]: any; };
|
||||
result: any;
|
||||
|
||||
constructor(message: string, status: number, response: string, headers: { [key: string]: any; }, result: any) {
|
||||
super();
|
||||
|
||||
this.message = message;
|
||||
this.status = status;
|
||||
this.response = response;
|
||||
this.headers = headers;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
protected isApiException = true;
|
||||
|
||||
static isApiException(obj: any): obj is ApiException {
|
||||
return obj.isApiException === true;
|
||||
}
|
||||
}
|
||||
|
||||
function throwException(message: string, status: number, response: string, headers: { [key: string]: any; }, result?: any): any {
|
||||
if (result !== null && result !== undefined)
|
||||
throw result;
|
||||
else
|
||||
throw new ApiException(message, status, response, headers, null);
|
||||
}
|
128
Birdmap.API/ClientApp/src/components/logs/Logs.jsx
Normal file
128
Birdmap.API/ClientApp/src/components/logs/Logs.jsx
Normal file
@ -0,0 +1,128 @@
|
||||
import { Box, Button, Checkbox, List, ListItem, ListItemIcon, ListItemText, Paper, withStyles } from '@material-ui/core';
|
||||
import { blueGrey } from '@material-ui/core/colors';
|
||||
import React, { Component } from 'react';
|
||||
import LogService from './LogService';
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
padding: '64px',
|
||||
backgroundColor: theme.palette.primary.dark,
|
||||
},
|
||||
paper: {
|
||||
backgroundColor: blueGrey[50],
|
||||
},
|
||||
});
|
||||
|
||||
class Logs extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
service: null,
|
||||
files: [],
|
||||
checked: [],
|
||||
selectAllChecked: false,
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
var service = new LogService();
|
||||
this.setState({service: service});
|
||||
|
||||
service.getAll().then(result => {
|
||||
this.setState({files: result});
|
||||
}).catch(ex => console.log(ex));
|
||||
}
|
||||
|
||||
handleToggle = (value) => {
|
||||
const currentIndex = this.state.checked.indexOf(value);
|
||||
const newChecked = [...this.state.checked];
|
||||
|
||||
if (currentIndex === -1) {
|
||||
newChecked.push(value);
|
||||
} else {
|
||||
newChecked.splice(currentIndex, 1);
|
||||
}
|
||||
|
||||
this.setState({checked: newChecked});
|
||||
}
|
||||
|
||||
handleSelectAllToggle = () => {
|
||||
this.setState({selectAllChecked: !this.state.selectAllChecked});
|
||||
if (this.state.selectAllChecked) {
|
||||
this.setState({checked: []});
|
||||
} else {
|
||||
const newChecked = [...this.state.files];
|
||||
this.setState({checked: newChecked});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onDownload = () => {
|
||||
this.state.service.getFiles(this.state.checked)
|
||||
.then(result => {
|
||||
const filename = `Logs-${new Date().toISOString()}.zip`;
|
||||
const textUrl = URL.createObjectURL(result.data);
|
||||
const element = document.createElement('a');
|
||||
element.setAttribute('href', textUrl);
|
||||
element.setAttribute('download', filename);
|
||||
element.style.display = 'none';
|
||||
document.body.appendChild(element);
|
||||
element.click();
|
||||
document.body.removeChild(element);
|
||||
this.setState({checked: []});
|
||||
this.setState({selectAllChecked: false});
|
||||
})
|
||||
.catch(ex => console.log(ex));
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
|
||||
const Files = this.state.files.map((value) => {
|
||||
const labelId = `checkbox-list-label-${value}`;
|
||||
|
||||
return (
|
||||
<ListItem key={value} role={undefined} dense button onClick={() => this.handleToggle(value)}>
|
||||
<ListItemIcon>
|
||||
<Checkbox
|
||||
edge="start"
|
||||
checked={this.state.checked.indexOf(value) !== -1}
|
||||
tabIndex={-1}
|
||||
disableRipple
|
||||
inputProps={{ 'aria-labelledby': labelId }}
|
||||
/>
|
||||
</ListItemIcon>
|
||||
<ListItemText id={labelId} primary={`${value}`} />
|
||||
</ListItem>
|
||||
);
|
||||
})
|
||||
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
<Paper className={classes.paper}>
|
||||
<List className={classes.paper}>
|
||||
<ListItem key="Select-all" role={undefined} dense button onClick={this.handleSelectAllToggle}>
|
||||
<ListItemIcon>
|
||||
<Checkbox
|
||||
edge="start"
|
||||
checked={this.state.selectAllChecked}
|
||||
tabIndex={-1}
|
||||
disableRipple
|
||||
inputProps={{ 'aria-labelledby': "Select-all" }}
|
||||
/>
|
||||
</ListItemIcon>
|
||||
<ListItemText id="checkbox-list-label-Select-all" primary={(this.state.selectAllChecked ? "Uns" : "S") + "elect all"} />
|
||||
</ListItem>
|
||||
{Files}
|
||||
</List>
|
||||
<Button onClick={this.onDownload}>
|
||||
Download
|
||||
</Button>
|
||||
</Paper>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(Logs);
|
12
Birdmap.API/ClientApp/src/contexts/DevicesContext.js
Normal file
12
Birdmap.API/ClientApp/src/contexts/DevicesContext.js
Normal file
@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
|
||||
export default React.createContext({
|
||||
devices: [],
|
||||
heatmapPoints: [],
|
||||
|
||||
addHandler: (_, __) => { },
|
||||
removeHandler: (_, __) => { },
|
||||
|
||||
updateDevice: () => { },
|
||||
updateAllDevices: () => { },
|
||||
});
|
152
Birdmap.API/ClientApp/src/contexts/DevicesContextProvider.js
Normal file
152
Birdmap.API/ClientApp/src/contexts/DevicesContextProvider.js
Normal file
@ -0,0 +1,152 @@
|
||||
import Context from './DevicesContext'
|
||||
import { HubConnectionBuilder } from '@microsoft/signalr';
|
||||
import DeviceService from '../common/DeviceService';
|
||||
import C from '../common/Constants'
|
||||
import React, { Component } from 'react'
|
||||
|
||||
const hub_url = '/hubs/devices';
|
||||
|
||||
export default class DevicesContextProvider extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const handlers = {};
|
||||
for (var property in C) {
|
||||
handlers[C[property]] = [];
|
||||
};
|
||||
|
||||
this.state = {
|
||||
hubConnection: null,
|
||||
devices: [],
|
||||
heatmapPoints: [],
|
||||
handlers: handlers,
|
||||
};
|
||||
}
|
||||
|
||||
addHandler = (methodName, handler) => {
|
||||
const updatedHandlers = this.state.handlers;
|
||||
updatedHandlers[methodName].push(handler);
|
||||
//console.log("Added '" + methodName + "' handler.");
|
||||
this.setState({ handlers: updatedHandlers });
|
||||
}
|
||||
|
||||
removeHandler = (methodName, handler) => {
|
||||
const updatedHandlers = this.state.handlers;
|
||||
var index = updatedHandlers[methodName].findIndex((h => h === handler));
|
||||
if (index > -1) {
|
||||
updatedHandlers[methodName].splice(index, 1);
|
||||
//console.log("Removed '" + methodName + "' handler.");
|
||||
}
|
||||
this.setState({ handlers: updatedHandlers });
|
||||
}
|
||||
|
||||
updateDevice = (id) => {
|
||||
this.updateDeviceInternal(id);
|
||||
}
|
||||
|
||||
updateAllDevices = () => {
|
||||
this.updateAllDevicesInternal();
|
||||
}
|
||||
|
||||
invokeHandlers(methodName, context) {
|
||||
this.state.handlers[methodName].forEach(function (handler) {
|
||||
handler(context);
|
||||
});
|
||||
}
|
||||
|
||||
updateAllDevicesInternal(service = null) {
|
||||
if (service === null) {
|
||||
service = new DeviceService();
|
||||
}
|
||||
service.getall().then(result => {
|
||||
this.setState({ devices: result });
|
||||
this.invokeHandlers(C.update_all_method_name, null);
|
||||
}).catch(ex => {
|
||||
console.log(ex);
|
||||
});
|
||||
}
|
||||
|
||||
updateDeviceInternal(id, service = null) {
|
||||
if (service === null) {
|
||||
service = new DeviceService();
|
||||
}
|
||||
service.getdevice(id).then(result => {
|
||||
const updatedDevices = [...this.state.devices];
|
||||
var index = updatedDevices.findIndex((d => d.id == id));
|
||||
if (index > -1) {
|
||||
updatedDevices[index] = result;
|
||||
}
|
||||
else {
|
||||
updatedDevices.push(result);
|
||||
}
|
||||
this.setState({ devices: updatedDevices });
|
||||
this.invokeHandlers(C.update_method_name, result);
|
||||
}).catch(ex => console.log("Device update failed.", ex));
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const service = new DeviceService();
|
||||
this.updateAllDevicesInternal(service);
|
||||
|
||||
const newConnection = new HubConnectionBuilder()
|
||||
.withUrl(hub_url)
|
||||
.withAutomaticReconnect()
|
||||
.build();
|
||||
|
||||
this.setState({ hubConnection: newConnection });
|
||||
|
||||
newConnection.start()
|
||||
.then(_ => {
|
||||
console.log('Devices hub Connected!');
|
||||
|
||||
newConnection.on(C.probability_method_name, (messages) => {
|
||||
//console.log(method_name + " recieved: [id: " + id + ", date: " + date + ", prob: " + prob + "]");
|
||||
const newPoints = [];
|
||||
for (var message of messages) {
|
||||
var device = this.state.devices.filter(function (x) { return x.id === message.deviceId })[0]
|
||||
var newPoint = { deviceId: device.id, lat: device.coordinates.latitude, lng: device.coordinates.longitude, prob: message.probability, date: new Date(message.date) };
|
||||
newPoints.push(newPoint);
|
||||
}
|
||||
this.setState({
|
||||
heatmapPoints: this.state.heatmapPoints.concat(newPoints)
|
||||
});
|
||||
|
||||
this.invokeHandlers(C.probability_method_name, newPoints);
|
||||
});
|
||||
|
||||
newConnection.on(C.update_all_method_name, () => {
|
||||
this.updateAllDevicesInternal(service);
|
||||
});
|
||||
|
||||
newConnection.on(C.update_method_name, (id) => this.updateDeviceInternal(id, service));
|
||||
}).catch(e => console.log('Devices hub Connection failed: ', e));
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.state.hubConnection != null) {
|
||||
this.state.hubConnection.off(C.probability_method_name);
|
||||
this.state.hubConnection.off(C.update_all_method_name);
|
||||
this.state.hubConnection.off(C.update_method_name);
|
||||
console.log('Devices hub Disconnected!');
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Context.Provider
|
||||
value={{
|
||||
devices: this.state.devices,
|
||||
heatmapPoints: this.state.heatmapPoints,
|
||||
|
||||
addHandler: this.addHandler,
|
||||
removeHandler: this.removeHandler,
|
||||
|
||||
updateDevice: this.updateDevice,
|
||||
updateAllDevices: this.updateAllDevices,
|
||||
}}
|
||||
>
|
||||
{this.props.children}
|
||||
</Context.Provider>
|
||||
);
|
||||
};
|
||||
}
|
@ -60,7 +60,7 @@ namespace Birdmap.Controllers
|
||||
var token = tokenHandler.CreateToken(tokenDescriptor);
|
||||
var tokenString = tokenHandler.WriteToken(token);
|
||||
|
||||
var response = _mapper.Map<AuthenticateResponse>(user);
|
||||
AuthenticateResponse response = _mapper.Map<AuthenticateResponse>(user);
|
||||
response.AccessToken = tokenString;
|
||||
response.TokenType = "Bearer";
|
||||
response.ExpiresIn = expiresInSeconds;
|
||||
|
@ -1,26 +1,30 @@
|
||||
using Birdmap.BLL.Interfaces;
|
||||
using Birdmap.BLL.Services.CommunicationServices.Hubs;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Birdmap.API.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[Authorize(Roles = "User, Admin")]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class DevicesController : ControllerBase
|
||||
{
|
||||
private readonly IDeviceService _service;
|
||||
private readonly IHubContext<DevicesHub, IDevicesHubClient> _hubContext;
|
||||
private readonly ILogger<ServicesController> _logger;
|
||||
|
||||
public DevicesController(IDeviceService service, ILogger<ServicesController> logger)
|
||||
public DevicesController(IDeviceService service, IHubContext<DevicesHub, IDevicesHubClient> hubContext, ILogger<ServicesController> logger)
|
||||
{
|
||||
_service = service;
|
||||
_hubContext = hubContext;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -36,24 +40,28 @@ namespace Birdmap.API.Controllers
|
||||
|
||||
/// <summary>Shut down all devices</summary>
|
||||
/// <returns>Message sent</returns>
|
||||
[Authorize(Roles = "Admin")]
|
||||
[HttpPost, Route("offline")]
|
||||
public async Task<IActionResult> Offlineall()
|
||||
{
|
||||
_logger.LogInformation("Turning off all devices and sensors...");
|
||||
|
||||
await _service.OfflineallAsync();
|
||||
await _hubContext.Clients.All.NotifyAllUpdatedAsync();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>Bring all devices online</summary>
|
||||
/// <returns>Message sent</returns>
|
||||
[Authorize(Roles = "Admin")]
|
||||
[HttpPost, Route("online")]
|
||||
public async Task<IActionResult> Onlineall()
|
||||
{
|
||||
_logger.LogInformation("Turning on all devices and sensors...");
|
||||
|
||||
await _service.OnlineallAsync();
|
||||
await _hubContext.Clients.All.NotifyAllUpdatedAsync();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
@ -72,12 +80,14 @@ namespace Birdmap.API.Controllers
|
||||
/// <summary>Shut down device</summary>
|
||||
/// <param name="deviceID">ID of device to shut down</param>
|
||||
/// <returns>Message sent</returns>
|
||||
[Authorize(Roles = "Admin")]
|
||||
[HttpPost, Route("{deviceID}/offline")]
|
||||
public async Task<IActionResult> Offlinedevice([BindRequired] Guid deviceID)
|
||||
{
|
||||
_logger.LogInformation($"Turning off device [{deviceID}]...");
|
||||
|
||||
await _service.OfflinedeviceAsync(deviceID);
|
||||
await _hubContext.Clients.All.NotifyDeviceUpdatedAsync(deviceID);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
@ -85,12 +95,14 @@ namespace Birdmap.API.Controllers
|
||||
/// <summary>Bring device online</summary>
|
||||
/// <param name="deviceID">ID of device to bring online</param>
|
||||
/// <returns>Message sent</returns>
|
||||
[Authorize(Roles = "Admin")]
|
||||
[HttpPost, Route("{deviceID}/online")]
|
||||
public async Task<IActionResult> Onlinedevice([BindRequired] Guid deviceID)
|
||||
{
|
||||
_logger.LogInformation($"Turning on device [{deviceID}]...");
|
||||
|
||||
await _service.OnlinedeviceAsync(deviceID);
|
||||
await _hubContext.Clients.All.NotifyDeviceUpdatedAsync(deviceID);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
@ -111,12 +123,14 @@ namespace Birdmap.API.Controllers
|
||||
/// <param name="deviceID">ID of device to query</param>
|
||||
/// <param name="sensorID">ID of sensor to query</param>
|
||||
/// <returns>Message sent</returns>
|
||||
[Authorize(Roles = "Admin")]
|
||||
[HttpPost, Route("{deviceID}/{sensorID}/offline")]
|
||||
public async Task<IActionResult> Offlinesensor([BindRequired] Guid deviceID, [BindRequired] Guid sensorID)
|
||||
{
|
||||
_logger.LogInformation($"Turning off sensor [{sensorID}] of device [{deviceID}]...");
|
||||
|
||||
await _service.OfflinesensorAsync(deviceID, sensorID);
|
||||
await _hubContext.Clients.All.NotifyDeviceUpdatedAsync(deviceID);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
@ -125,12 +139,14 @@ namespace Birdmap.API.Controllers
|
||||
/// <param name="deviceID">ID of device to query</param>
|
||||
/// <param name="sensorID">ID of sensor to query</param>
|
||||
/// <returns>Message sent</returns>
|
||||
[Authorize(Roles = "Admin")]
|
||||
[HttpPost, Route("{deviceID}/{sensorID}/online")]
|
||||
public async Task<IActionResult> Onlinesensor([BindRequired] Guid deviceID, [BindRequired] Guid sensorID)
|
||||
{
|
||||
_logger.LogInformation($"Turning on sensor [{sensorID}] of device [{deviceID}]...");
|
||||
|
||||
await _service.OnlinesensorAsync(deviceID, sensorID);
|
||||
await _hubContext.Clients.All.NotifyDeviceUpdatedAsync(deviceID);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
66
Birdmap.API/Controllers/LogsController.cs
Normal file
66
Birdmap.API/Controllers/LogsController.cs
Normal file
@ -0,0 +1,66 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Birdmap.API.Controllers
|
||||
{
|
||||
[Authorize(Roles = "Admin")]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class LogsController : ControllerBase
|
||||
{
|
||||
private readonly ILogger<LogsController> _logger;
|
||||
private readonly string _logFolderPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Logs");
|
||||
|
||||
public LogsController(ILogger<LogsController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet("all")]
|
||||
public ActionResult<List<string>> GetAll()
|
||||
{
|
||||
_logger.LogInformation($"Getting all log filenames from folder: '{_logFolderPath}'...");
|
||||
|
||||
return Directory.EnumerateFiles(_logFolderPath, "*.log")
|
||||
.Select(f => Path.GetFileName(f))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetFiles([FromQuery] params string[] filenames)
|
||||
{
|
||||
if (!filenames.Any())
|
||||
return null;
|
||||
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
var zipStream = new MemoryStream();
|
||||
|
||||
using (var zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
|
||||
{
|
||||
foreach (var file in Directory.GetFiles(_logFolderPath, "*.log"))
|
||||
{
|
||||
var filename = Path.GetFileName(file);
|
||||
|
||||
if (filenames.Contains(filename))
|
||||
{
|
||||
zip.CreateEntryFromFile(file, filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zipStream.Position = 0;
|
||||
|
||||
return File(zipStream, "application/octet-stream");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,45 +1,64 @@
|
||||
using AutoMapper;
|
||||
using Birdmap.API.DTOs;
|
||||
using Birdmap.BLL.Interfaces;
|
||||
using Birdmap.BLL.Services.CommunicationServices.Hubs;
|
||||
using Birdmap.DAL.Entities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Birdmap.API.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[Authorize(Roles = "User, Admin")]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class ServicesController : ControllerBase
|
||||
{
|
||||
private readonly IServiceService _service;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly ICommunicationService _communicationService;
|
||||
private readonly IHubContext<ServicesHub, IServicesHubClient> _hubContext;
|
||||
private readonly ILogger<ServicesController> _logger;
|
||||
|
||||
public ServicesController(IServiceService service, IMapper mapper, ILogger<ServicesController> logger)
|
||||
public ServicesController(IServiceService service, IMapper mapper, ICommunicationServiceProvider communicationServiceProvider,
|
||||
IHubContext<ServicesHub, IServicesHubClient> hubContext, ILogger<ServicesController> logger)
|
||||
{
|
||||
_service = service;
|
||||
_mapper = mapper;
|
||||
_communicationService = communicationServiceProvider.Service;
|
||||
_hubContext = hubContext;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet("count"), ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<int>> GetCountAsync()
|
||||
{
|
||||
_logger.LogInformation($"Getting service count from db...");
|
||||
|
||||
return await _service.GetServiceCountAsync() + 1;
|
||||
}
|
||||
|
||||
[HttpGet, ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<List<ServiceInfo>>> GetAsync()
|
||||
{
|
||||
_logger.LogInformation($"Getting all services from db...");
|
||||
var serviceInfos = (await _service.GetAllServicesAsync())
|
||||
.Select(s => new ServiceInfo { Service = _mapper.Map<ServiceRequest>(s) });
|
||||
.Select(s => new ServiceInfo { Service = _mapper.Map<ServiceRequest>(s) }).ToList();
|
||||
|
||||
var client = new HttpClient();
|
||||
var tasks = new List<Task>();
|
||||
foreach (var si in serviceInfos)
|
||||
{
|
||||
tasks.Add(Task.Run(async () =>
|
||||
{
|
||||
var client = new HttpClient();
|
||||
try
|
||||
{
|
||||
_logger.LogInformation($"Sending a request to service [{si.Service.Name}] with url [{si.Service.Uri}]...");
|
||||
@ -50,14 +69,30 @@ namespace Birdmap.API.Controllers
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning($"Requesting service [{si.Service.Name}] faulted.");
|
||||
si.StatusCode = System.Net.HttpStatusCode.ServiceUnavailable;
|
||||
si.StatusCode = HttpStatusCode.ServiceUnavailable;
|
||||
si.Response = ex.ToString();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
|
||||
serviceInfos.Add(new()
|
||||
{
|
||||
Service = new()
|
||||
{
|
||||
Id = 0,
|
||||
Name = "Message Queue Service",
|
||||
Uri = "localhost",
|
||||
},
|
||||
Response = $"IsConnected: {_communicationService.IsConnected}",
|
||||
StatusCode = _communicationService.IsConnected ? HttpStatusCode.OK : HttpStatusCode.ServiceUnavailable,
|
||||
});
|
||||
|
||||
return serviceInfos.ToList();
|
||||
}
|
||||
|
||||
[Authorize(Roles = "Admin")]
|
||||
[HttpPost, ProducesResponseType(StatusCodes.Status201Created)]
|
||||
public async Task<ActionResult<ServiceRequest>> PostAsync(ServiceRequest request)
|
||||
{
|
||||
@ -66,12 +101,14 @@ namespace Birdmap.API.Controllers
|
||||
_mapper.Map<Service>(request));
|
||||
|
||||
_logger.LogInformation($"Created service [{created.Id}].");
|
||||
await _hubContext.Clients.All.NotifyUpdatedAsync();
|
||||
|
||||
return CreatedAtAction(
|
||||
nameof(GetAsync),
|
||||
_mapper.Map<ServiceRequest>(created));
|
||||
}
|
||||
|
||||
[Authorize(Roles = "Admin")]
|
||||
[HttpPut, ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
public async Task<IActionResult> PutAsync(ServiceRequest request)
|
||||
{
|
||||
@ -80,16 +117,19 @@ namespace Birdmap.API.Controllers
|
||||
service.IsFromConfig = false;
|
||||
|
||||
await _service.UpdateServiceAsync(service);
|
||||
await _hubContext.Clients.All.NotifyUpdatedAsync();
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[Authorize(Roles = "Admin")]
|
||||
[HttpDelete("{id}"), ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
public async Task<IActionResult> DeleteAsync(int id)
|
||||
{
|
||||
_logger.LogInformation($"Deleting service [{id}]...");
|
||||
|
||||
await _service.DeleteServiceAsync(id);
|
||||
await _hubContext.Clients.All.NotifyUpdatedAsync();
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Birdmap.Models
|
||||
{
|
||||
public class AuthenticateRequest
|
||||
public record AuthenticateRequest
|
||||
{
|
||||
[Required(AllowEmptyStrings = false, ErrorMessage = "Username is required.")]
|
||||
public string Username { get; set; }
|
||||
|
@ -3,7 +3,7 @@ using Newtonsoft.Json;
|
||||
|
||||
namespace Birdmap.API.DTOs
|
||||
{
|
||||
public class AuthenticateResponse
|
||||
public record AuthenticateResponse
|
||||
{
|
||||
[JsonProperty("user_name")]
|
||||
public string Username { get; set; }
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Birdmap.API.DTOs
|
||||
{
|
||||
public class RegisterRequest
|
||||
public record RegisterRequest
|
||||
{
|
||||
[Required(AllowEmptyStrings = false, ErrorMessage = "Username is required.")]
|
||||
public string Username { get; set; }
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Birdmap.API.DTOs
|
||||
{
|
||||
public class ServiceInfo
|
||||
public record ServiceInfo
|
||||
{
|
||||
public ServiceRequest Service { get; set; }
|
||||
public HttpStatusCode StatusCode { get; set; }
|
||||
|
@ -1,6 +1,6 @@
|
||||
namespace Birdmap.API.DTOs
|
||||
{
|
||||
public class ServiceRequest
|
||||
public record ServiceRequest
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
@ -1,6 +1,6 @@
|
||||
using Birdmap.API;
|
||||
using Birdmap.DAL;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@ -8,7 +8,7 @@ using NLog;
|
||||
using NLog.Web;
|
||||
using System;
|
||||
|
||||
namespace Birdmap
|
||||
namespace Birdmap.API
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
@ -40,6 +40,10 @@ namespace Birdmap
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||
{
|
||||
config.AddEnvironmentVariables(prefix: "Birdmap_");
|
||||
})
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseStartup<Startup>();
|
||||
@ -54,8 +58,8 @@ namespace Birdmap
|
||||
private static void SeedDatabase(IHost host)
|
||||
{
|
||||
using var scope = host.Services.CreateScope();
|
||||
var dbInitializer = scope.ServiceProvider.GetRequiredService<DbInitializer>();
|
||||
|
||||
var dbInitializer = scope.ServiceProvider.GetRequiredService<DbInitializer>();
|
||||
dbInitializer.Initialize();
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
{
|
||||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iis": {
|
||||
"applicationUrl": "http://localhost/Birdmap.API",
|
||||
"sslPort": 0
|
||||
},
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:63288",
|
||||
"sslPort": 44331
|
||||
@ -12,16 +16,24 @@
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"Birdmap_LocalDbConnectionString": "Data Source=DESKTOP-3600;Initial Catalog=birdmap2;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"Birdmap": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:5001;http://localhost:5000",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"applicationUrl": "https://localhost:5001;http://localhost:5000"
|
||||
},
|
||||
"Docker": {
|
||||
"commandName": "Docker",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
|
||||
"publishAllPorts": true,
|
||||
"useSSL": true
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
using AutoMapper;
|
||||
using Birdmap.API;
|
||||
using Birdmap.API.Middlewares;
|
||||
using Birdmap.BLL;
|
||||
using Birdmap.BLL.Services.CommunicationServices.Hubs;
|
||||
using Birdmap.DAL;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
@ -11,9 +11,11 @@ using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using NSwag.Generation.Processors.Security;
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Birdmap
|
||||
namespace Birdmap.API
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
@ -48,7 +50,7 @@ namespace Birdmap
|
||||
})
|
||||
.AddJwtBearer(opt =>
|
||||
{
|
||||
// opt.RequireHttpsMetadata = false;
|
||||
//opt.RequireHttpsMetadata = false;
|
||||
opt.SaveToken = true;
|
||||
opt.IncludeErrorDetails = true;
|
||||
opt.TokenValidationParameters = new TokenValidationParameters
|
||||
@ -65,6 +67,20 @@ namespace Birdmap
|
||||
{
|
||||
configuration.RootPath = "ClientApp/build";
|
||||
});
|
||||
|
||||
services.AddSwaggerDocument(opt =>
|
||||
{
|
||||
opt.Title = "Birdmap";
|
||||
opt.OperationProcessors.Add(new OperationSecurityScopeProcessor("Jwt Token"));
|
||||
opt.AddSecurity("Jwt Token", Array.Empty<string>(),
|
||||
new NSwag.OpenApiSecurityScheme
|
||||
{
|
||||
Type = NSwag.OpenApiSecuritySchemeType.ApiKey,
|
||||
Name = "Authorization",
|
||||
In = NSwag.OpenApiSecurityApiKeyLocation.Header,
|
||||
Description = "Bearer {token}",
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
@ -74,24 +90,25 @@ namespace Birdmap
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseMiddleware<ExceptionHandlerMiddleware>();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
app.UseMiddleware<ExceptionHandlerMiddleware>();
|
||||
|
||||
app.UseOpenApi();
|
||||
app.UseSwaggerUi3();
|
||||
|
||||
app.UseStaticFiles();
|
||||
app.UseSpaStaticFiles();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthorization();
|
||||
app.UseAuthentication();
|
||||
app.UseRouting();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapHealthChecks("/health");
|
||||
endpoints.MapControllers();
|
||||
endpoints.MapHub<DevicesHub>("/hubs/devices");
|
||||
endpoints.MapHub<ServicesHub>("/hubs/services");
|
||||
});
|
||||
|
||||
app.UseSpa(spa =>
|
||||
|
@ -5,5 +5,51 @@
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"Kestrel": {
|
||||
"Certificates": {
|
||||
"Default": {
|
||||
"Password": "certpass123",
|
||||
"Path": "C:\\Users\\Ricsi\\AppData\\Roaming\\ASP.NET\\Https\\aspnetapp.pfx"
|
||||
}
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"Secret": "7vj.3KW.hYE!}4u6",
|
||||
// "LocalDbConnectionString": "Data Source=DESKTOP-3600\\SQLEXPRESS;Initial Catalog=birdmap;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
|
||||
"LocalDbConnectionString": "Data Source=DESKTOP-3600;Initial Catalog=birdmap2;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
|
||||
"Defaults": {
|
||||
"Services": {
|
||||
"Local Database": "https://localhost:44331/health",
|
||||
"KMLabz Services": "https://birb.k8s.kmlabz.com/devices"
|
||||
},
|
||||
"Users": [
|
||||
{
|
||||
"Name": "admin",
|
||||
"Password": "pass",
|
||||
"Role": "Admin"
|
||||
},
|
||||
{
|
||||
"Name": "user",
|
||||
"Password": "pass",
|
||||
"Role": "User"
|
||||
}
|
||||
]
|
||||
},
|
||||
"UseDummyServices": true,
|
||||
"ServicesBaseUrl": "https://birb.k8s.kmlabz.com/",
|
||||
"UseRabbitMq": false,
|
||||
"Mqtt": {
|
||||
"BrokerHostSettings": {
|
||||
"Host": "localhost",
|
||||
"Port": 1883
|
||||
},
|
||||
|
||||
"ClientSettings": {
|
||||
"Id": "ASP.NET Core client",
|
||||
"Username": "username",
|
||||
"Password": "password",
|
||||
"Topic": "devices/output"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,27 +6,51 @@
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"Kestrel": {
|
||||
"Certificates": {
|
||||
"Default": {
|
||||
"Password": "",
|
||||
"Path": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"Secret": "7vj.3KW.hYE!}4u6",
|
||||
// "LocalDbConnectionString": "Data Source=DESKTOP-3600\\SQLEXPRESS;Initial Catalog=birdmap;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
|
||||
"LocalDbConnectionString": "Data Source=DESKTOP-3600;Initial Catalog=birdmap;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
|
||||
"Secret": "",
|
||||
"LocalDbConnectionString": "",
|
||||
"Defaults": {
|
||||
"Services": {
|
||||
"Local Database": "https://localhost:44331/health",
|
||||
"KMLabz Services": "https://birb.k8s.kmlabz.com/devices"
|
||||
},
|
||||
"Users": [
|
||||
{
|
||||
"Name": "admin",
|
||||
"Password": "pass",
|
||||
"Role": "Admin"
|
||||
"Users": []
|
||||
},
|
||||
{
|
||||
"Name": "user",
|
||||
"Password": "pass",
|
||||
"Role": "User"
|
||||
"UseDummyServices": false,
|
||||
"ServicesBaseUrl": "https://birb.k8s.kmlabz.com/",
|
||||
"UseRabbitMq": false,
|
||||
"Mqtt": {
|
||||
"BrokerHostSettings": {
|
||||
"VirtualHost": "",
|
||||
"Host": "",
|
||||
"Port": 1883
|
||||
},
|
||||
|
||||
"ExchangeSettings": {
|
||||
"Name": "",
|
||||
"Type": "",
|
||||
"Durable": false,
|
||||
"AutoDelete": false
|
||||
},
|
||||
|
||||
"QueueSettings": {
|
||||
"Name": "",
|
||||
"Durable": false,
|
||||
"Exclusive": false,
|
||||
"AutoDelete": false
|
||||
},
|
||||
|
||||
"ClientSettings": {
|
||||
"Id": "ASP.NET Core client",
|
||||
"Username": "",
|
||||
"Password": "",
|
||||
"Topic": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"UseDummyServices": true
|
||||
}
|
||||
|
5
Birdmap.API/libman.json
Normal file
5
Birdmap.API/libman.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"defaultProvider": "cdnjs",
|
||||
"libraries": []
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
autoReload="true"
|
||||
internalLogLevel="Info"
|
||||
internalLogFile="${basedir}Log/internal-nlog.txt"
|
||||
internalLogFile="${basedir}Logs/internal-nlog.txt"
|
||||
throwConfigExceptions="true">
|
||||
|
||||
<!-- enable asp.net core layout renderers -->
|
||||
@ -14,22 +14,36 @@
|
||||
<!-- the targets to write to -->
|
||||
<targets async="true">
|
||||
<default-target-parameters xsi:type="File" keepFileOpen="false" maxArchiveFiles="10" archiveAboveSize="1048576"/>
|
||||
<target xsi:type="File" name="allfile" fileName="${basedir}Log/birdmap-all-${shortdate}.log"
|
||||
layout="${longdate} [${event-properties:item=EventId_Id}] ${uppercase:${level}} ${logger} - ${message} ${exception:format=tostring}" />
|
||||
<target xsi:type="File" name="allFile" fileName="${basedir}Logs/birdmap-all-${shortdate}.log"
|
||||
layout="${longdate} [${threadname:whenEmpty=${threadid}}] ${uppercase:${level}} ${logger} - ${message} ${exception:format=tostring}" />
|
||||
|
||||
<target xsi:type="File" name="mqttFile" fileName="${basedir}Logs/birdmap-mqtt-${shortdate}.log"
|
||||
layout="${longdate} [${threadname:whenEmpty=${threadid}}] ${uppercase:${level}} ${logger} - ${message} ${exception:format=tostring}" />
|
||||
|
||||
<target xsi:type="File" name="hubsFile" fileName="${basedir}Logs/birdmap-hubs-${shortdate}.log"
|
||||
layout="${longdate} [${threadname:whenEmpty=${threadid}}] ${uppercase:${level}} ${logger} - ${message} ${exception:format=tostring}" />
|
||||
|
||||
<!-- another file log, only own logs. Uses some ASP.NET core renderers -->
|
||||
<target xsi:type="File" name="ownFile" fileName="${basedir}Log/birdmap-own-${shortdate}.log"
|
||||
layout="${longdate} [${event-properties:item=EventId_Id}] ${uppercase:${level}} ${callsite} - ${message} ${exception:format=tostring} (url: ${aspnet-request-url})(action: ${aspnet-mvc-action})" />
|
||||
<target xsi:type="File" name="ownFile" fileName="${basedir}Logs/birdmap-own-${shortdate}.log"
|
||||
layout="${longdate} [${threadname:whenEmpty=${threadid}}] ${uppercase:${level}} ${callsite} - ${message} ${exception:format=tostring} (url: ${aspnet-request-url})(action: ${aspnet-mvc-action})" />
|
||||
</targets>
|
||||
|
||||
<!-- rules to map from logger name to target +-->
|
||||
<rules>
|
||||
<!--All logs, including from Microsoft-->
|
||||
<logger name="*" minlevel="Trace" writeTo="allfile" />
|
||||
<logger name="*" minlevel="Trace" writeTo="allFile" />
|
||||
|
||||
<!--Skip non-critical Microsoft logs and so log only own logs-->
|
||||
<!--Skip non-critical Mqtt logs-->
|
||||
<logger name="*.*Mqtt*.*" minlevel="Trace" maxlevel="Warning" writeTo="mqttFile" final="true"/>
|
||||
<logger name="*.*RabbitMq*.*" minlevel="Trace" maxlevel="Warning" writeTo="mqttFile" final="true"/>
|
||||
<logger name="*.*CommunicationServiceBase*.*" minlevel="Trace" maxlevel="Warning" writeTo="mqttFile" final="true"/>
|
||||
|
||||
<!--Skip non-critical Hub logs-->
|
||||
<logger name="*.*Hubs*.*" minlevel="Trace" maxlevel="Warning" writeTo="hubsFile" final="true"/>
|
||||
|
||||
<!--Skip non-critical Microsoft logs-->
|
||||
<logger name="Microsoft.*" maxlevel="Info" final="true" />
|
||||
<!-- BlackHole without writeTo -->
|
||||
|
||||
<logger name="*" minlevel="Trace" writeTo="ownFile" />
|
||||
</rules>
|
||||
</nlog>
|
@ -1,9 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Core" Version="1.1.0" />
|
||||
<PackageReference Include="MQTTnet" Version="3.0.13" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="RabbitMQ.Client" Version="6.2.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Birdmap.Common\Birdmap.Common.csproj" />
|
||||
<ProjectReference Include="..\Birdmap.DAL\Birdmap.DAL.csproj" />
|
||||
|
16
Birdmap.BLL/Helpers/IEnumerableExtensions.cs
Normal file
16
Birdmap.BLL/Helpers/IEnumerableExtensions.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Birdmap.BLL.Helpers
|
||||
{
|
||||
public static class IEnumerableExtensions
|
||||
{
|
||||
public static TSource RandomElementAt<TSource>(this IEnumerable<TSource> source, Random random = null)
|
||||
{
|
||||
random ??= new Random();
|
||||
|
||||
return source.ElementAt(random.Next(source.Count()));
|
||||
}
|
||||
}
|
||||
}
|
9
Birdmap.BLL/Interfaces/ICommunicationService.cs
Normal file
9
Birdmap.BLL/Interfaces/ICommunicationService.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Birdmap.BLL.Interfaces
|
||||
{
|
||||
public interface ICommunicationService : IHostedService
|
||||
{
|
||||
public bool IsConnected { get; }
|
||||
}
|
||||
}
|
7
Birdmap.BLL/Interfaces/ICommunicationServiceProvider.cs
Normal file
7
Birdmap.BLL/Interfaces/ICommunicationServiceProvider.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Birdmap.BLL.Interfaces
|
||||
{
|
||||
public interface ICommunicationServiceProvider
|
||||
{
|
||||
public ICommunicationService Service { get; }
|
||||
}
|
||||
}
|
163
Birdmap.BLL/Interfaces/IInputService.cs
Normal file
163
Birdmap.BLL/Interfaces/IInputService.cs
Normal file
@ -0,0 +1,163 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Birdmap.BLL.Interfaces
|
||||
{
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.8.2.0 (NJsonSchema v10.2.1.0 (Newtonsoft.Json v12.0.0.0))")]
|
||||
public partial interface IInputService
|
||||
{
|
||||
/// <summary>Get input object by ID</summary>
|
||||
/// <param name="tagID">ID of input object file</param>
|
||||
/// <returns>input object</returns>
|
||||
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||
System.Threading.Tasks.Task<InputSingeResponse> GetInputAsync(System.Guid tagID);
|
||||
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||
/// <summary>Get input object by ID</summary>
|
||||
/// <param name="tagID">ID of input object file</param>
|
||||
/// <returns>input object</returns>
|
||||
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||
System.Threading.Tasks.Task<InputSingeResponse> GetInputAsync(System.Guid tagID, System.Threading.CancellationToken cancellationToken);
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.2.1.0 (Newtonsoft.Json v12.0.0.0)")]
|
||||
public partial class InputSingeResponse
|
||||
{
|
||||
[Newtonsoft.Json.JsonProperty("status", Required = Newtonsoft.Json.Required.Always)]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
public string Status { get; set; }
|
||||
|
||||
[Newtonsoft.Json.JsonProperty("message", Required = Newtonsoft.Json.Required.Always)]
|
||||
[System.ComponentModel.DataAnnotations.Required]
|
||||
public InputObject Message { get; set; } = new InputObject();
|
||||
|
||||
private System.Collections.Generic.IDictionary<string, object> _additionalProperties = new System.Collections.Generic.Dictionary<string, object>();
|
||||
|
||||
[Newtonsoft.Json.JsonExtensionData]
|
||||
public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
|
||||
{
|
||||
get { return _additionalProperties; }
|
||||
set { _additionalProperties = value; }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.2.1.0 (Newtonsoft.Json v12.0.0.0)")]
|
||||
public partial class InputResponse : System.Collections.ObjectModel.Collection<InputObject>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.2.1.0 (Newtonsoft.Json v12.0.0.0)")]
|
||||
public partial class InputObject
|
||||
{
|
||||
[Newtonsoft.Json.JsonProperty("tag", Required = Newtonsoft.Json.Required.Always)]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
public System.Guid Tag { get; set; }
|
||||
|
||||
[Newtonsoft.Json.JsonProperty("date", Required = Newtonsoft.Json.Required.Always)]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
[Newtonsoft.Json.JsonConverter(typeof(DateFormatConverter))]
|
||||
public System.DateTimeOffset Date { get; set; }
|
||||
|
||||
[Newtonsoft.Json.JsonProperty("device_id", Required = Newtonsoft.Json.Required.Always)]
|
||||
public Guid Device_id { get; set; }
|
||||
|
||||
private System.Collections.Generic.IDictionary<string, object> _additionalProperties = new System.Collections.Generic.Dictionary<string, object>();
|
||||
|
||||
[Newtonsoft.Json.JsonExtensionData]
|
||||
public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
|
||||
{
|
||||
get { return _additionalProperties; }
|
||||
set { _additionalProperties = value; }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.2.1.0 (Newtonsoft.Json v12.0.0.0)")]
|
||||
public partial class ApiResponse
|
||||
{
|
||||
[Newtonsoft.Json.JsonProperty("status", Required = Newtonsoft.Json.Required.Always)]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
public string Status { get; set; }
|
||||
|
||||
[Newtonsoft.Json.JsonProperty("message", Required = Newtonsoft.Json.Required.Always)]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
public string Message { get; set; }
|
||||
|
||||
private System.Collections.Generic.IDictionary<string, object> _additionalProperties = new System.Collections.Generic.Dictionary<string, object>();
|
||||
|
||||
[Newtonsoft.Json.JsonExtensionData]
|
||||
public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
|
||||
{
|
||||
get { return _additionalProperties; }
|
||||
set { _additionalProperties = value; }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.2.1.0 (Newtonsoft.Json v12.0.0.0)")]
|
||||
public partial class Description
|
||||
{
|
||||
[Newtonsoft.Json.JsonProperty("deviceid", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
|
||||
public string Deviceid { get; set; }
|
||||
|
||||
[Newtonsoft.Json.JsonProperty("date", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
|
||||
[Newtonsoft.Json.JsonConverter(typeof(DateFormatConverter))]
|
||||
public System.DateTimeOffset Date { get; set; }
|
||||
|
||||
private System.Collections.Generic.IDictionary<string, object> _additionalProperties = new System.Collections.Generic.Dictionary<string, object>();
|
||||
|
||||
[Newtonsoft.Json.JsonExtensionData]
|
||||
public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
|
||||
{
|
||||
get { return _additionalProperties; }
|
||||
set { _additionalProperties = value; }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.2.1.0 (Newtonsoft.Json v12.0.0.0)")]
|
||||
internal class DateFormatConverter : Newtonsoft.Json.Converters.IsoDateTimeConverter
|
||||
{
|
||||
public DateFormatConverter()
|
||||
{
|
||||
DateTimeFormat = "yyyy-MM-dd";
|
||||
}
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.8.2.0 (NJsonSchema v10.2.1.0 (Newtonsoft.Json v12.0.0.0))")]
|
||||
public partial class FileParameter
|
||||
{
|
||||
public FileParameter(System.IO.Stream data)
|
||||
: this(data, null, null)
|
||||
{
|
||||
}
|
||||
|
||||
public FileParameter(System.IO.Stream data, string fileName)
|
||||
: this(data, fileName, null)
|
||||
{
|
||||
}
|
||||
|
||||
public FileParameter(System.IO.Stream data, string fileName, string contentType)
|
||||
{
|
||||
Data = data;
|
||||
FileName = fileName;
|
||||
ContentType = contentType;
|
||||
}
|
||||
|
||||
public System.IO.Stream Data { get; private set; }
|
||||
|
||||
public string FileName { get; private set; }
|
||||
|
||||
public string ContentType { get; private set; }
|
||||
}
|
||||
|
||||
}
|
13
Birdmap.BLL/Interfaces/IMqttClientService.cs
Normal file
13
Birdmap.BLL/Interfaces/IMqttClientService.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Disconnecting;
|
||||
using MQTTnet.Client.Receiving;
|
||||
|
||||
namespace Birdmap.BLL.Interfaces
|
||||
{
|
||||
public interface IMqttClientService : IMqttClientConnectedHandler,
|
||||
IMqttClientDisconnectedHandler,
|
||||
IMqttApplicationMessageReceivedHandler
|
||||
{
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ namespace Birdmap.BLL.Interfaces
|
||||
{
|
||||
public interface IServiceService
|
||||
{
|
||||
Task<int> GetServiceCountAsync();
|
||||
Task<List<Service>> GetAllServicesAsync();
|
||||
Task<Service> GetServiceAsync(int id);
|
||||
Task<Service> CreateServiceAsync(Service service);
|
||||
|
22
Birdmap.BLL/Options/MqttClientOptions.cs
Normal file
22
Birdmap.BLL/Options/MqttClientOptions.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using MQTTnet.Client.Options;
|
||||
using System;
|
||||
|
||||
namespace Birdmap.BLL.Options
|
||||
{
|
||||
public class MqttClientOptions : MqttClientOptionsBuilder
|
||||
{
|
||||
public IServiceProvider ServiceProvider { get; }
|
||||
|
||||
public MqttClientOptions(IServiceProvider serviceProvider)
|
||||
{
|
||||
ServiceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public MqttClientOptions WithTopic(string topic)
|
||||
{
|
||||
WithUserProperty("Topic", topic);
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
11
Birdmap.BLL/Options/RabbitMqClientOptions.cs
Normal file
11
Birdmap.BLL/Options/RabbitMqClientOptions.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace Birdmap.BLL.Options
|
||||
{
|
||||
public record RabbitMqClientOptions(
|
||||
string Hostname, int Port, string VirtualHost,
|
||||
string Username, string Password,
|
||||
string ExchangeName, string ExchangeType,
|
||||
bool ExchangeDurable, bool ExchangeAutoDelete,
|
||||
string QueueName,
|
||||
bool QueueDurable, bool QueueAutoDelete, bool QueueExclusive,
|
||||
string Topic);
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
using Birdmap.BLL.Interfaces;
|
||||
using Birdmap.BLL.Services.CommunicationServices.Hubs;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace Birdmap.BLL.Services.CommunationServices
|
||||
{
|
||||
internal class Payload
|
||||
{
|
||||
[JsonProperty("tag")]
|
||||
public Guid TagID { get; set; }
|
||||
|
||||
[JsonProperty("probability")]
|
||||
public double Probability { get; set; }
|
||||
}
|
||||
|
||||
internal abstract class CommunicationServiceBase : ICommunicationService
|
||||
{
|
||||
protected readonly ILogger _logger;
|
||||
protected readonly IInputService _inputService;
|
||||
protected readonly IHubContext<DevicesHub, IDevicesHubClient> _hubContext;
|
||||
private readonly Timer _hubTimer;
|
||||
private readonly List<Message> _messages = new();
|
||||
private readonly object _messageLock = new();
|
||||
|
||||
public abstract bool IsConnected { get; }
|
||||
|
||||
public CommunicationServiceBase(ILogger logger, IInputService inputService, IHubContext<DevicesHub, IDevicesHubClient> hubContext)
|
||||
{
|
||||
_logger = logger;
|
||||
_inputService = inputService;
|
||||
_hubContext = hubContext;
|
||||
_hubTimer = new Timer()
|
||||
{
|
||||
AutoReset = true,
|
||||
Interval = 1000,
|
||||
};
|
||||
_hubTimer.Elapsed += SendMqttMessagesWithSignalR;
|
||||
}
|
||||
|
||||
protected async Task ProcessJsonMessageAsync(string json)
|
||||
{
|
||||
try
|
||||
{
|
||||
var payload = JsonConvert.DeserializeObject<Payload>(json);
|
||||
var inputResponse = await _inputService.GetInputAsync(payload.TagID);
|
||||
|
||||
lock (_messageLock)
|
||||
{
|
||||
_messages.Add(new Message(inputResponse.Message.Device_id, inputResponse.Message.Date.UtcDateTime, payload.Probability));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Could not handle application message.");
|
||||
}
|
||||
}
|
||||
|
||||
protected void StartMessageTimer()
|
||||
{
|
||||
_hubTimer.Start();
|
||||
}
|
||||
protected void StopMessageTimer()
|
||||
{
|
||||
_hubTimer.Stop();
|
||||
}
|
||||
|
||||
private void SendMqttMessagesWithSignalR(object sender, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
lock (_messageLock)
|
||||
{
|
||||
if (_messages.Any())
|
||||
{
|
||||
_logger.LogInformation($"Sending ({_messages.Count}) messages: {string.Join(" | ", _messages)}");
|
||||
_hubContext.Clients.All.NotifyMessagesAsync(_messages);
|
||||
_messages.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract Task StartAsync(CancellationToken cancellationToken);
|
||||
|
||||
public abstract Task StopAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
using Birdmap.BLL.Interfaces;
|
||||
|
||||
namespace Birdmap.BLL.Services.CommunicationServices
|
||||
{
|
||||
internal class CommunicationServiceProvider : ICommunicationServiceProvider
|
||||
{
|
||||
public ICommunicationService Service { get; }
|
||||
|
||||
public CommunicationServiceProvider(ICommunicationService service)
|
||||
{
|
||||
Service = service;
|
||||
}
|
||||
}
|
||||
}
|
31
Birdmap.BLL/Services/CommunationServices/Hubs/DevicesHub.cs
Normal file
31
Birdmap.BLL/Services/CommunationServices/Hubs/DevicesHub.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Birdmap.BLL.Services.CommunicationServices.Hubs
|
||||
{
|
||||
public class DevicesHub : Hub<IDevicesHubClient>
|
||||
{
|
||||
private readonly ILogger<DevicesHub> _logger;
|
||||
|
||||
public DevicesHub(ILogger<DevicesHub> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override Task OnConnectedAsync()
|
||||
{
|
||||
_logger.LogInformation("Devices Hub Client connected.");
|
||||
|
||||
return base.OnConnectedAsync();
|
||||
}
|
||||
|
||||
public override Task OnDisconnectedAsync(Exception exception)
|
||||
{
|
||||
_logger.LogInformation("Devices Hub Client disconnected.");
|
||||
|
||||
return base.OnDisconnectedAsync(exception);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Birdmap.BLL.Services.CommunicationServices.Hubs
|
||||
{
|
||||
public record Message(Guid DeviceId, DateTime Date, double Probability);
|
||||
|
||||
public interface IDevicesHubClient
|
||||
{
|
||||
Task NotifyMessagesAsync(IEnumerable<Message> messages);
|
||||
Task NotifyDeviceUpdatedAsync(Guid deviceId);
|
||||
Task NotifyAllUpdatedAsync();
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Birdmap.BLL.Services.CommunicationServices.Hubs
|
||||
{
|
||||
public interface IServicesHubClient
|
||||
{
|
||||
Task NotifyUpdatedAsync();
|
||||
}
|
||||
}
|
31
Birdmap.BLL/Services/CommunationServices/Hubs/ServicesHub.cs
Normal file
31
Birdmap.BLL/Services/CommunationServices/Hubs/ServicesHub.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Birdmap.BLL.Services.CommunicationServices.Hubs
|
||||
{
|
||||
public class ServicesHub : Hub<IServicesHubClient>
|
||||
{
|
||||
private readonly ILogger<ServicesHub> _logger;
|
||||
|
||||
public ServicesHub(ILogger<ServicesHub> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override Task OnConnectedAsync()
|
||||
{
|
||||
_logger.LogInformation("Services Hub Client connected.");
|
||||
|
||||
return base.OnConnectedAsync();
|
||||
}
|
||||
|
||||
public override Task OnDisconnectedAsync(Exception exception)
|
||||
{
|
||||
_logger.LogInformation("Services Hub Client disconnected.");
|
||||
|
||||
return base.OnDisconnectedAsync(exception);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
using Birdmap.BLL.Interfaces;
|
||||
using Birdmap.BLL.Services.CommunationServices;
|
||||
using Birdmap.BLL.Services.CommunicationServices.Hubs;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Disconnecting;
|
||||
using MQTTnet.Client.Options;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Birdmap.BLL.Services.CommunicationServices.Mqtt
|
||||
{
|
||||
internal class MqttClientService : CommunicationServiceBase, IMqttClientService
|
||||
{
|
||||
private readonly IMqttClient _mqttClient;
|
||||
private readonly IMqttClientOptions _options;
|
||||
|
||||
public override bool IsConnected => _mqttClient.IsConnected;
|
||||
|
||||
public MqttClientService(IMqttClientOptions options, ILogger<MqttClientService> logger, IInputService inputService, IHubContext<DevicesHub, IDevicesHubClient> hubContext)
|
||||
: base(logger, inputService, hubContext)
|
||||
{
|
||||
_options = options;
|
||||
|
||||
_mqttClient = new MqttFactory().CreateMqttClient();
|
||||
ConfigureMqttClient();
|
||||
}
|
||||
|
||||
private void ConfigureMqttClient()
|
||||
{
|
||||
_mqttClient.ConnectedHandler = this;
|
||||
_mqttClient.DisconnectedHandler = this;
|
||||
_mqttClient.ApplicationMessageReceivedHandler = this;
|
||||
}
|
||||
|
||||
public Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
{
|
||||
var message = eventArgs.ApplicationMessage.ConvertPayloadToString();
|
||||
|
||||
_logger.LogDebug($"Recieved [{eventArgs.ClientId}] " +
|
||||
$"Topic: {eventArgs.ApplicationMessage.Topic} | Payload: {message} | QoS: {eventArgs.ApplicationMessage.QualityOfServiceLevel} | Retain: {eventArgs.ApplicationMessage.Retain}");
|
||||
|
||||
return ProcessJsonMessageAsync(message);
|
||||
}
|
||||
|
||||
public async Task HandleConnectedAsync(MqttClientConnectedEventArgs eventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
var topic = _options.UserProperties.SingleOrDefault(up => up.Name == "Topic")?.Value;
|
||||
_logger.LogInformation($"Connected. Auth result: {eventArgs.AuthenticateResult}. Subscribing to topic: {topic}");
|
||||
|
||||
await _mqttClient.SubscribeAsync(topic);
|
||||
StartMessageTimer();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Cannot subscribe...");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task HandleDisconnectedAsync(MqttClientDisconnectedEventArgs eventArgs)
|
||||
{
|
||||
_logger.LogDebug(eventArgs.Exception, $"Disconnected. Reason {eventArgs.ReasonCode}. Auth result: {eventArgs.AuthenticateResult}. Reconnecting...");
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||
|
||||
try
|
||||
{
|
||||
StopMessageTimer();
|
||||
await _mqttClient.ConnectAsync(_options, CancellationToken.None);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogDebug(ex, $"Reconnect failed...");
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _mqttClient.ConnectAsync(_options);
|
||||
if (!_mqttClient.IsConnected)
|
||||
{
|
||||
await _mqttClient.ReconnectAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Cannot connect...");
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
var disconnectOption = new MqttClientDisconnectOptions
|
||||
{
|
||||
ReasonCode = MqttClientDisconnectReason.NormalDisconnection,
|
||||
ReasonString = "NormalDiconnection"
|
||||
};
|
||||
await _mqttClient.DisconnectAsync(disconnectOption, cancellationToken);
|
||||
}
|
||||
await _mqttClient.DisconnectAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Cannot disconnect...");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
using Birdmap.BLL.Interfaces;
|
||||
using Birdmap.BLL.Options;
|
||||
using Birdmap.BLL.Services.CommunicationServices.Hubs;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RabbitMQ.Client;
|
||||
using RabbitMQ.Client.Events;
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Birdmap.BLL.Services.CommunationServices.RabbitMq
|
||||
{
|
||||
internal class RabbitMqClientService : CommunicationServiceBase
|
||||
{
|
||||
private IConnection _connection;
|
||||
private IModel _channel;
|
||||
private readonly IConnectionFactory _factory;
|
||||
private readonly RabbitMqClientOptions _options;
|
||||
|
||||
public override bool IsConnected => _connection.IsOpen;
|
||||
|
||||
public RabbitMqClientService(RabbitMqClientOptions options, ILogger<RabbitMqClientService> logger, IInputService inputService, IHubContext<DevicesHub, IDevicesHubClient> hubContext)
|
||||
: base(logger, inputService, hubContext)
|
||||
{
|
||||
_options = options;
|
||||
_factory = new ConnectionFactory()
|
||||
{
|
||||
HostName = options.Hostname,
|
||||
Port = options.Port,
|
||||
UserName = options.Username,
|
||||
Password = options.Password,
|
||||
|
||||
AutomaticRecoveryEnabled = true,
|
||||
};
|
||||
}
|
||||
|
||||
private Task OnRecieved(object sender, BasicDeliverEventArgs eventArgs)
|
||||
{
|
||||
var props = eventArgs.BasicProperties;
|
||||
var body = Encoding.UTF8.GetString(eventArgs.Body.ToArray());
|
||||
|
||||
_logger.LogDebug($"Recieved [{props.UserId}] " +
|
||||
$"ConsumerTag: {eventArgs.ConsumerTag} | DeliveryTag: {eventArgs.DeliveryTag} | Payload: {body} | Priority: {props.Priority}");
|
||||
|
||||
return ProcessJsonMessageAsync(body);
|
||||
}
|
||||
|
||||
public override async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
Connect();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Cannot connect. Reconnecting...");
|
||||
await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await StartAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
public override Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
StopMessageTimer();
|
||||
_channel?.Close();
|
||||
_connection?.Close();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Cannot disconnect...");
|
||||
return Task.FromException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void Connect()
|
||||
{
|
||||
|
||||
_connection = _factory.CreateConnection();
|
||||
_channel = _connection.CreateModel();
|
||||
|
||||
_channel.ExchangeDeclare(
|
||||
exchange: _options.ExchangeName,
|
||||
type: _options.ExchangeType,
|
||||
durable: _options.ExchangeDurable,
|
||||
autoDelete: _options.ExchangeAutoDelete);
|
||||
|
||||
_channel.QueueDeclare(
|
||||
queue: _options.QueueName,
|
||||
durable: _options.QueueDurable,
|
||||
exclusive: _options.QueueExclusive,
|
||||
autoDelete: _options.QueueAutoDelete);
|
||||
|
||||
_channel.QueueBind(queue: _options.QueueName,
|
||||
exchange: _options.ExchangeName,
|
||||
routingKey: _options.Topic);
|
||||
|
||||
var consumer = new AsyncEventingBasicConsumer(_channel);
|
||||
consumer.Received += OnRecieved;
|
||||
|
||||
_channel.BasicConsume(queue: _options.QueueName,
|
||||
autoAck: true,
|
||||
consumer: consumer);
|
||||
|
||||
StartMessageTimer();
|
||||
}
|
||||
}
|
||||
}
|
@ -6,8 +6,12 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Birdmap.BLL.Services
|
||||
{
|
||||
public abstract class DeviceServiceBase : IDeviceService
|
||||
public abstract class DeviceAndInputServiceBase : IDeviceService, IInputService
|
||||
{
|
||||
public virtual Task<InputSingeResponse> GetInputAsync(Guid tagID)
|
||||
=> GetInputAsync(tagID, CancellationToken.None);
|
||||
public abstract Task<InputSingeResponse> GetInputAsync(Guid tagID, CancellationToken cancellationToken);
|
||||
|
||||
public virtual Task<ICollection<Device>> GetallAsync()
|
||||
=> GetallAsync(CancellationToken.None);
|
||||
public abstract Task<ICollection<Device>> GetallAsync(CancellationToken cancellationToken);
|
@ -1,4 +1,5 @@
|
||||
using Birdmap.BLL.Interfaces;
|
||||
using Birdmap.BLL.Helpers;
|
||||
using Birdmap.BLL.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -7,48 +8,55 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Birdmap.BLL.Services
|
||||
{
|
||||
public class DummyDeviceService : DeviceServiceBase
|
||||
public class DummyDeviceAndInputService : DeviceAndInputServiceBase
|
||||
{
|
||||
private const int numberOfDevices = 15;
|
||||
|
||||
private const double centerLong = 21.469640;
|
||||
private const double centerLat = 48.275939;
|
||||
private const double radius = 0.000200;
|
||||
private const double radius = 0.001;
|
||||
|
||||
private static readonly Random Rand = new();
|
||||
|
||||
private static readonly Lazy<ICollection<Device>> Devices = new(GenerateDevices);
|
||||
|
||||
private static readonly Dictionary<Guid, InputSingeResponse> TagToInput = new();
|
||||
private static readonly object InputLock = new();
|
||||
|
||||
private readonly Lazy<ICollection<Device>> _devices = new Lazy<ICollection<Device>>(GenerateDevices);
|
||||
private static ListOfDevices GenerateDevices()
|
||||
{
|
||||
var devices = new ListOfDevices();
|
||||
var rand = new Random();
|
||||
|
||||
T GetRandomEnum<T>()
|
||||
{
|
||||
var values = Enum.GetValues(typeof(T));
|
||||
return (T)values.GetValue(rand.Next(values.Length));
|
||||
return (T)values.GetValue(Rand.Next(values.Length));
|
||||
}
|
||||
|
||||
double GetPlusMinus(double center, double radius)
|
||||
{
|
||||
return center - radius + rand.NextDouble() * radius * 2;
|
||||
return center - radius + Rand.NextDouble() * radius * 2;
|
||||
}
|
||||
|
||||
for (int d = 0; d < 15; d++)
|
||||
for (int d = 0; d < numberOfDevices; d++)
|
||||
{
|
||||
var sensors = new ArrayofSensors();
|
||||
for (int s = 0; s < rand.Next(1, 5); s++)
|
||||
for (int s = 0; s < Rand.Next(1, 6); s++)
|
||||
{
|
||||
sensors.Add(new Sensor
|
||||
sensors.Add(new()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Status = GetRandomEnum<SensorStatus>(),
|
||||
});
|
||||
}
|
||||
|
||||
devices.Add(new Device
|
||||
devices.Add(new()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Sensors = sensors,
|
||||
Status = GetRandomEnum<DeviceStatus>(),
|
||||
Url = "dummyservice.device.url",
|
||||
Coordinates = new Coordinates
|
||||
Coordinates = new()
|
||||
{
|
||||
Latitude = GetPlusMinus(centerLat, radius),
|
||||
Longitude = GetPlusMinus(centerLong, radius),
|
||||
@ -61,17 +69,17 @@ namespace Birdmap.BLL.Services
|
||||
|
||||
public override Task<ICollection<Device>> GetallAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(_devices.Value);
|
||||
return Task.FromResult(Devices.Value);
|
||||
}
|
||||
|
||||
public override Task<Device> GetdeviceAsync(Guid deviceID, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(_devices.Value.SingleOrDefault(d => d.Id == deviceID));
|
||||
return Task.FromResult(Devices.Value.SingleOrDefault(d => d.Id == deviceID));
|
||||
}
|
||||
|
||||
public override Task<Sensor> GetsensorAsync(Guid deviceID, Guid sensorID, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(_devices.Value.SingleOrDefault(d => d.Id == deviceID)?.Sensors.SingleOrDefault(s => s.Id == sensorID));
|
||||
return Task.FromResult(Devices.Value.SingleOrDefault(d => d.Id == deviceID)?.Sensors.SingleOrDefault(s => s.Id == sensorID));
|
||||
}
|
||||
|
||||
public override Task OfflineallAsync(CancellationToken cancellationToken)
|
||||
@ -114,7 +122,7 @@ namespace Birdmap.BLL.Services
|
||||
|
||||
private void SetStatus(DeviceStatus deviceStatus, SensorStatus sensorStatus)
|
||||
{
|
||||
foreach (var device in _devices.Value)
|
||||
foreach (var device in Devices.Value)
|
||||
{
|
||||
device.Status = deviceStatus;
|
||||
foreach (var sensor in device.Sensors)
|
||||
@ -135,5 +143,29 @@ namespace Birdmap.BLL.Services
|
||||
var sensor = GetsensorAsync(deviceId, sensorID).Result;
|
||||
sensor.Status = status;
|
||||
}
|
||||
|
||||
public override Task<InputSingeResponse> GetInputAsync(Guid tagID, CancellationToken cancellationToken)
|
||||
{
|
||||
lock (InputLock)
|
||||
{
|
||||
if (!TagToInput.TryGetValue(tagID, out var value))
|
||||
{
|
||||
value = new()
|
||||
{
|
||||
Status = "Dummy_OK",
|
||||
Message = new()
|
||||
{
|
||||
Tag = tagID,
|
||||
Date = DateTime.Now,
|
||||
Device_id = Devices.Value.Where(d => d.Status == DeviceStatus.Online).RandomElementAt(Rand).Id,
|
||||
}
|
||||
};
|
||||
|
||||
TagToInput.TryAdd(tagID, value);
|
||||
}
|
||||
|
||||
return Task.FromResult(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?'
|
||||
#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ...
|
||||
#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..."
|
||||
#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type '...' is never equal to 'null' of type '...?'"
|
||||
|
||||
namespace Birdmap.BLL.Services
|
||||
{
|
||||
@ -16,14 +17,15 @@ namespace Birdmap.BLL.Services
|
||||
using System = global::System;
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.8.2.0 (NJsonSchema v10.2.1.0 (Newtonsoft.Json v12.0.0.0))")]
|
||||
public partial class LiveDummyService : IDeviceService
|
||||
public partial class LiveDeviceService : IDeviceService
|
||||
{
|
||||
private string _baseUrl = "https://birb.k8s.kmlabz.com";
|
||||
private System.Net.Http.HttpClient _httpClient;
|
||||
private System.Lazy<Newtonsoft.Json.JsonSerializerSettings> _settings;
|
||||
|
||||
public LiveDummyService(System.Net.Http.HttpClient httpClient)
|
||||
public LiveDeviceService(string baseUrl, System.Net.Http.HttpClient httpClient)
|
||||
{
|
||||
_baseUrl = baseUrl;
|
||||
_httpClient = httpClient;
|
||||
_settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings);
|
||||
}
|
||||
@ -895,6 +897,7 @@ namespace Birdmap.BLL.Services
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning restore 8703
|
||||
#pragma warning restore 1591
|
||||
#pragma warning restore 1573
|
||||
#pragma warning restore 472
|
||||
|
480
Birdmap.BLL/Services/LiveInputService.cs
Normal file
480
Birdmap.BLL/Services/LiveInputService.cs
Normal file
@ -0,0 +1,480 @@
|
||||
//----------------------
|
||||
// <auto-generated>
|
||||
// Generated using the NSwag toolchain v13.8.2.0 (NJsonSchema v10.2.1.0 (Newtonsoft.Json v12.0.0.0)) (http://NSwag.org)
|
||||
// </auto-generated>
|
||||
//----------------------
|
||||
|
||||
#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended."
|
||||
#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword."
|
||||
#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?'
|
||||
#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ...
|
||||
#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..."
|
||||
#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type '...' is never equal to 'null' of type '...?'"
|
||||
|
||||
namespace Birdmap.BLL.Services
|
||||
{
|
||||
using Birdmap.BLL.Interfaces;
|
||||
using System = global::System;
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.8.2.0 (NJsonSchema v10.2.1.0 (Newtonsoft.Json v12.0.0.0))")]
|
||||
public partial class LiveInputService : IInputService
|
||||
{
|
||||
private string _baseUrl = "https://birb.k8s.kmlabz.com";
|
||||
private System.Net.Http.HttpClient _httpClient;
|
||||
private System.Lazy<Newtonsoft.Json.JsonSerializerSettings> _settings;
|
||||
|
||||
public LiveInputService(string baseUrl, System.Net.Http.HttpClient httpClient)
|
||||
{
|
||||
_baseUrl = baseUrl;
|
||||
_httpClient = httpClient;
|
||||
_settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings);
|
||||
}
|
||||
|
||||
private Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings()
|
||||
{
|
||||
var settings = new Newtonsoft.Json.JsonSerializerSettings();
|
||||
UpdateJsonSerializerSettings(settings);
|
||||
return settings;
|
||||
}
|
||||
|
||||
public string BaseUrl
|
||||
{
|
||||
get { return _baseUrl; }
|
||||
set { _baseUrl = value; }
|
||||
}
|
||||
|
||||
protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _settings.Value; } }
|
||||
|
||||
partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings);
|
||||
partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url);
|
||||
partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder);
|
||||
partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response);
|
||||
|
||||
/// <summary>Get all stored input queries</summary>
|
||||
/// <returns>Array of input objects</returns>
|
||||
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||
public System.Threading.Tasks.Task<System.Collections.Generic.ICollection<InputObject>> GetallAsync()
|
||||
{
|
||||
return GetallAsync(System.Threading.CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||
/// <summary>Get all stored input queries</summary>
|
||||
/// <returns>Array of input objects</returns>
|
||||
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||
public async System.Threading.Tasks.Task<System.Collections.Generic.ICollection<InputObject>> GetallAsync(System.Threading.CancellationToken cancellationToken)
|
||||
{
|
||||
var urlBuilder_ = new System.Text.StringBuilder();
|
||||
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/sample");
|
||||
|
||||
var client_ = _httpClient;
|
||||
var disposeClient_ = false;
|
||||
try
|
||||
{
|
||||
using (var request_ = new System.Net.Http.HttpRequestMessage())
|
||||
{
|
||||
request_.Method = new System.Net.Http.HttpMethod("GET");
|
||||
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json"));
|
||||
|
||||
PrepareRequest(client_, request_, urlBuilder_);
|
||||
var url_ = urlBuilder_.ToString();
|
||||
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
|
||||
PrepareRequest(client_, request_, url_);
|
||||
|
||||
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||
var disposeResponse_ = true;
|
||||
try
|
||||
{
|
||||
var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
|
||||
if (response_.Content != null && response_.Content.Headers != null)
|
||||
{
|
||||
foreach (var item_ in response_.Content.Headers)
|
||||
headers_[item_.Key] = item_.Value;
|
||||
}
|
||||
|
||||
ProcessResponse(client_, response_);
|
||||
|
||||
var status_ = (int)response_.StatusCode;
|
||||
if (status_ == 200)
|
||||
{
|
||||
var objectResponse_ = await ReadObjectResponseAsync<System.Collections.Generic.ICollection<InputObject>>(response_, headers_).ConfigureAwait(false);
|
||||
if (objectResponse_.Object == null)
|
||||
{
|
||||
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||
}
|
||||
return objectResponse_.Object;
|
||||
}
|
||||
else
|
||||
if (status_ == 404)
|
||||
{
|
||||
var objectResponse_ = await ReadObjectResponseAsync<ApiResponse>(response_, headers_).ConfigureAwait(false);
|
||||
if (objectResponse_.Object == null)
|
||||
{
|
||||
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||
}
|
||||
throw new ApiException<ApiResponse>("No object matching filter", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (disposeResponse_)
|
||||
response_.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (disposeClient_)
|
||||
client_.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>uploads a sample into the system</summary>
|
||||
/// <returns>successful operation</returns>
|
||||
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||
public System.Threading.Tasks.Task<ApiResponse> UploadFileAsync(Description description, FileParameter file)
|
||||
{
|
||||
return UploadFileAsync(description, file, System.Threading.CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||
/// <summary>uploads a sample into the system</summary>
|
||||
/// <returns>successful operation</returns>
|
||||
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||
public async System.Threading.Tasks.Task<ApiResponse> UploadFileAsync(Description description, FileParameter file, System.Threading.CancellationToken cancellationToken)
|
||||
{
|
||||
var urlBuilder_ = new System.Text.StringBuilder();
|
||||
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/sample");
|
||||
|
||||
var client_ = _httpClient;
|
||||
var disposeClient_ = false;
|
||||
try
|
||||
{
|
||||
using (var request_ = new System.Net.Http.HttpRequestMessage())
|
||||
{
|
||||
var boundary_ = System.Guid.NewGuid().ToString();
|
||||
var content_ = new System.Net.Http.MultipartFormDataContent(boundary_);
|
||||
content_.Headers.Remove("Content-Type");
|
||||
content_.Headers.TryAddWithoutValidation("Content-Type", "multipart/form-data; boundary=" + boundary_);
|
||||
if (description == null)
|
||||
throw new System.ArgumentNullException("description");
|
||||
else
|
||||
{
|
||||
content_.Add(new System.Net.Http.StringContent(ConvertToString(description, System.Globalization.CultureInfo.InvariantCulture)), "description");
|
||||
}
|
||||
if (file == null)
|
||||
throw new System.ArgumentNullException("file");
|
||||
else
|
||||
{
|
||||
var content_file_ = new System.Net.Http.StreamContent(file.Data);
|
||||
if (!string.IsNullOrEmpty(file.ContentType))
|
||||
content_file_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse(file.ContentType);
|
||||
content_.Add(content_file_, "file", file.FileName ?? "file");
|
||||
}
|
||||
request_.Content = content_;
|
||||
request_.Method = new System.Net.Http.HttpMethod("POST");
|
||||
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json"));
|
||||
|
||||
PrepareRequest(client_, request_, urlBuilder_);
|
||||
var url_ = urlBuilder_.ToString();
|
||||
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
|
||||
PrepareRequest(client_, request_, url_);
|
||||
|
||||
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||
var disposeResponse_ = true;
|
||||
try
|
||||
{
|
||||
var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
|
||||
if (response_.Content != null && response_.Content.Headers != null)
|
||||
{
|
||||
foreach (var item_ in response_.Content.Headers)
|
||||
headers_[item_.Key] = item_.Value;
|
||||
}
|
||||
|
||||
ProcessResponse(client_, response_);
|
||||
|
||||
var status_ = (int)response_.StatusCode;
|
||||
if (status_ == 200)
|
||||
{
|
||||
var objectResponse_ = await ReadObjectResponseAsync<ApiResponse>(response_, headers_).ConfigureAwait(false);
|
||||
if (objectResponse_.Object == null)
|
||||
{
|
||||
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||
}
|
||||
return objectResponse_.Object;
|
||||
}
|
||||
else
|
||||
if (status_ == 400)
|
||||
{
|
||||
var objectResponse_ = await ReadObjectResponseAsync<ApiResponse>(response_, headers_).ConfigureAwait(false);
|
||||
if (objectResponse_.Object == null)
|
||||
{
|
||||
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||
}
|
||||
throw new ApiException<ApiResponse>("JSON parse error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
|
||||
}
|
||||
else
|
||||
if (status_ == 415)
|
||||
{
|
||||
var objectResponse_ = await ReadObjectResponseAsync<ApiResponse>(response_, headers_).ConfigureAwait(false);
|
||||
if (objectResponse_.Object == null)
|
||||
{
|
||||
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||
}
|
||||
throw new ApiException<ApiResponse>("Media type error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
|
||||
}
|
||||
else
|
||||
if (status_ == 417)
|
||||
{
|
||||
var objectResponse_ = await ReadObjectResponseAsync<ApiResponse>(response_, headers_).ConfigureAwait(false);
|
||||
if (objectResponse_.Object == null)
|
||||
{
|
||||
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||
}
|
||||
throw new ApiException<ApiResponse>("JSON invalid schema", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
|
||||
}
|
||||
else
|
||||
if (status_ == 469)
|
||||
{
|
||||
var objectResponse_ = await ReadObjectResponseAsync<ApiResponse>(response_, headers_).ConfigureAwait(false);
|
||||
if (objectResponse_.Object == null)
|
||||
{
|
||||
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||
}
|
||||
throw new ApiException<ApiResponse>("No file found", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
|
||||
}
|
||||
else
|
||||
if (status_ == 470)
|
||||
{
|
||||
var objectResponse_ = await ReadObjectResponseAsync<ApiResponse>(response_, headers_).ConfigureAwait(false);
|
||||
if (objectResponse_.Object == null)
|
||||
{
|
||||
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||
}
|
||||
throw new ApiException<ApiResponse>("Description missing", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (disposeResponse_)
|
||||
response_.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (disposeClient_)
|
||||
client_.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Get input object by ID</summary>
|
||||
/// <param name="tagID">ID of input object file</param>
|
||||
/// <returns>input object</returns>
|
||||
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||
public System.Threading.Tasks.Task<InputSingeResponse> GetInputAsync(System.Guid tagID)
|
||||
{
|
||||
return GetInputAsync(tagID, System.Threading.CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||
/// <summary>Get input object by ID</summary>
|
||||
/// <param name="tagID">ID of input object file</param>
|
||||
/// <returns>input object</returns>
|
||||
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||
public async System.Threading.Tasks.Task<InputSingeResponse> GetInputAsync(System.Guid tagID, System.Threading.CancellationToken cancellationToken)
|
||||
{
|
||||
if (tagID == null)
|
||||
throw new System.ArgumentNullException("tagID");
|
||||
|
||||
var urlBuilder_ = new System.Text.StringBuilder();
|
||||
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/sample/{tagID}");
|
||||
urlBuilder_.Replace("{tagID}", System.Uri.EscapeDataString(ConvertToString(tagID, System.Globalization.CultureInfo.InvariantCulture)));
|
||||
|
||||
var client_ = _httpClient;
|
||||
var disposeClient_ = false;
|
||||
try
|
||||
{
|
||||
using (var request_ = new System.Net.Http.HttpRequestMessage())
|
||||
{
|
||||
request_.Method = new System.Net.Http.HttpMethod("GET");
|
||||
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json"));
|
||||
|
||||
PrepareRequest(client_, request_, urlBuilder_);
|
||||
var url_ = urlBuilder_.ToString();
|
||||
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
|
||||
PrepareRequest(client_, request_, url_);
|
||||
|
||||
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||
var disposeResponse_ = true;
|
||||
try
|
||||
{
|
||||
var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
|
||||
if (response_.Content != null && response_.Content.Headers != null)
|
||||
{
|
||||
foreach (var item_ in response_.Content.Headers)
|
||||
headers_[item_.Key] = item_.Value;
|
||||
}
|
||||
|
||||
ProcessResponse(client_, response_);
|
||||
|
||||
var status_ = (int)response_.StatusCode;
|
||||
if (status_ == 200)
|
||||
{
|
||||
var objectResponse_ = await ReadObjectResponseAsync<InputSingeResponse>(response_, headers_).ConfigureAwait(false);
|
||||
if (objectResponse_.Object == null)
|
||||
{
|
||||
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||
}
|
||||
return objectResponse_.Object;
|
||||
}
|
||||
else
|
||||
if (status_ == 404)
|
||||
{
|
||||
var objectResponse_ = await ReadObjectResponseAsync<ApiResponse>(response_, headers_).ConfigureAwait(false);
|
||||
if (objectResponse_.Object == null)
|
||||
{
|
||||
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||
}
|
||||
throw new ApiException<ApiResponse>("Tag not found", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (disposeResponse_)
|
||||
response_.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (disposeClient_)
|
||||
client_.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
protected struct ObjectResponseResult<T>
|
||||
{
|
||||
public ObjectResponseResult(T responseObject, string responseText)
|
||||
{
|
||||
this.Object = responseObject;
|
||||
this.Text = responseText;
|
||||
}
|
||||
|
||||
public T Object { get; }
|
||||
|
||||
public string Text { get; }
|
||||
}
|
||||
|
||||
public bool ReadResponseAsString { get; set; }
|
||||
|
||||
protected virtual async System.Threading.Tasks.Task<ObjectResponseResult<T>> ReadObjectResponseAsync<T>(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers)
|
||||
{
|
||||
if (response == null || response.Content == null)
|
||||
{
|
||||
return new ObjectResponseResult<T>(default(T), string.Empty);
|
||||
}
|
||||
|
||||
if (ReadResponseAsString)
|
||||
{
|
||||
var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(responseText, JsonSerializerSettings);
|
||||
return new ObjectResponseResult<T>(typedBody, responseText);
|
||||
}
|
||||
catch (Newtonsoft.Json.JsonException exception)
|
||||
{
|
||||
var message = "Could not deserialize the response body string as " + typeof(T).FullName + ".";
|
||||
throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
|
||||
using (var streamReader = new System.IO.StreamReader(responseStream))
|
||||
using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader))
|
||||
{
|
||||
var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings);
|
||||
var typedBody = serializer.Deserialize<T>(jsonTextReader);
|
||||
return new ObjectResponseResult<T>(typedBody, string.Empty);
|
||||
}
|
||||
}
|
||||
catch (Newtonsoft.Json.JsonException exception)
|
||||
{
|
||||
var message = "Could not deserialize the response body stream as " + typeof(T).FullName + ".";
|
||||
throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (value is System.Enum)
|
||||
{
|
||||
var name = System.Enum.GetName(value.GetType(), value);
|
||||
if (name != null)
|
||||
{
|
||||
var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name);
|
||||
if (field != null)
|
||||
{
|
||||
var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute))
|
||||
as System.Runtime.Serialization.EnumMemberAttribute;
|
||||
if (attribute != null)
|
||||
{
|
||||
return attribute.Value != null ? attribute.Value : name;
|
||||
}
|
||||
}
|
||||
|
||||
return System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo));
|
||||
}
|
||||
}
|
||||
else if (value is bool)
|
||||
{
|
||||
return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant();
|
||||
}
|
||||
else if (value is byte[])
|
||||
{
|
||||
return System.Convert.ToBase64String((byte[])value);
|
||||
}
|
||||
else if (value.GetType().IsArray)
|
||||
{
|
||||
var array = System.Linq.Enumerable.OfType<object>((System.Array)value);
|
||||
return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo)));
|
||||
}
|
||||
|
||||
var result = System.Convert.ToString(value, cultureInfo);
|
||||
return (result is null) ? string.Empty : result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#pragma warning restore 8703
|
||||
#pragma warning restore 1591
|
||||
#pragma warning restore 1573
|
||||
#pragma warning restore 472
|
||||
#pragma warning restore 114
|
||||
#pragma warning restore 108
|
@ -17,6 +17,11 @@ namespace Birdmap.BLL.Services
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public Task<int> GetServiceCountAsync()
|
||||
{
|
||||
return _context.Services.CountAsync();
|
||||
}
|
||||
|
||||
public async Task<Service> CreateServiceAsync(Service service)
|
||||
{
|
||||
_context.Services.Add(service);
|
||||
|
@ -1,7 +1,14 @@
|
||||
using Birdmap.BLL.Interfaces;
|
||||
using Birdmap.BLL.Options;
|
||||
using Birdmap.BLL.Services;
|
||||
using Birdmap.BLL.Services.CommunationServices.RabbitMq;
|
||||
using Birdmap.BLL.Services.CommunicationServices;
|
||||
using Birdmap.BLL.Services.CommunicationServices.Mqtt;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Birdmap.BLL
|
||||
{
|
||||
@ -14,10 +21,134 @@ namespace Birdmap.BLL
|
||||
services.AddTransient<IServiceService, ServiceService>();
|
||||
|
||||
if (configuration.GetValue<bool>("UseDummyServices"))
|
||||
services.AddTransient<IDeviceService, DummyDeviceService>();
|
||||
{
|
||||
services.AddTransient<IInputService, DummyDeviceAndInputService>();
|
||||
services.AddTransient<IDeviceService, DummyDeviceAndInputService>();
|
||||
}
|
||||
else
|
||||
services.AddTransient<IDeviceService, LiveDummyService>();
|
||||
{
|
||||
var baseUrl = configuration.GetValue<string>("ServicesBaseUrl");
|
||||
|
||||
services.AddTransient<IInputService, LiveInputService>(serviceProvider =>
|
||||
{
|
||||
var httpClient = serviceProvider.GetService<HttpClient>();
|
||||
var service = new LiveInputService(baseUrl, httpClient);
|
||||
return service;
|
||||
});
|
||||
services.AddTransient<IDeviceService, LiveDeviceService>(serviceProvider =>
|
||||
{
|
||||
var httpClient = serviceProvider.GetService<HttpClient>();
|
||||
var service = new LiveDeviceService(baseUrl, httpClient);
|
||||
return service;
|
||||
});
|
||||
}
|
||||
|
||||
services.AddSignalR();
|
||||
|
||||
var mqtt = configuration.GetSection("Mqtt");
|
||||
|
||||
var client = mqtt.GetSection("ClientSettings");
|
||||
var clientSettings = new
|
||||
{
|
||||
Id = client.GetValue<string>("Id"),
|
||||
Username = client.GetValue<string>("Username"),
|
||||
Password = client.GetValue<string>("Password"),
|
||||
Topic = client.GetValue<string>("Topic"),
|
||||
};
|
||||
|
||||
var brokerHost = mqtt.GetSection("BrokerHostSettings");
|
||||
var brokerHostSettings = new
|
||||
{
|
||||
Host = brokerHost.GetValue<string>("Host"),
|
||||
Port = brokerHost.GetValue<int>("Port"),
|
||||
VirtualHost = brokerHost.GetValue<string>("VirtualHost"),
|
||||
};
|
||||
|
||||
var exchange = mqtt.GetSection("ExchangeSettings");
|
||||
var exchangeSettings = new
|
||||
{
|
||||
Name = exchange.GetValue<string>("Name"),
|
||||
Type = exchange.GetValue<string>("Type"),
|
||||
Durable = exchange.GetValue<bool>("Durable"),
|
||||
AutoDelete = exchange.GetValue<bool>("AutoDelete"),
|
||||
};
|
||||
|
||||
var queue = mqtt.GetSection("QueueSettings");
|
||||
var queueSettings = new
|
||||
{
|
||||
Name = queue.GetValue<string>("Name"),
|
||||
Durable = exchange.GetValue<bool>("Durable"),
|
||||
Exclusive = exchange.GetValue<bool>("Exclusive"),
|
||||
AutoDelete = exchange.GetValue<bool>("AutoDelete"),
|
||||
};
|
||||
|
||||
if (configuration.GetValue<bool>("UseRabbitMq"))
|
||||
{
|
||||
services.AddRabbitMqClientServiceWithConfig(new RabbitMqClientOptions(
|
||||
Hostname: brokerHostSettings.Host,
|
||||
Port: brokerHostSettings.Port,
|
||||
VirtualHost: brokerHostSettings.VirtualHost,
|
||||
Username: clientSettings.Username,
|
||||
Password: clientSettings.Password,
|
||||
ExchangeName: exchangeSettings.Name,
|
||||
ExchangeType: exchangeSettings.Type,
|
||||
ExchangeDurable: exchangeSettings.Durable,
|
||||
ExchangeAutoDelete: exchangeSettings.AutoDelete,
|
||||
QueueName: queueSettings.Name,
|
||||
QueueDurable: queueSettings.Durable,
|
||||
QueueExclusive: queueSettings.Exclusive,
|
||||
QueueAutoDelete: queueSettings.AutoDelete,
|
||||
Topic: clientSettings.Topic));
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddMqttClientServiceWithConfig(opt =>
|
||||
{
|
||||
opt
|
||||
.WithTopic(clientSettings.Topic)
|
||||
.WithCredentials(clientSettings.Username, clientSettings.Password)
|
||||
.WithClientId(clientSettings.Id)
|
||||
.WithTcpServer(brokerHostSettings.Host, brokerHostSettings.Port);
|
||||
});
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
private static IServiceCollection AddMqttClientServiceWithConfig(this IServiceCollection services, Action<MqttClientOptions> configureOptions)
|
||||
{
|
||||
services.AddSingleton(serviceProvider =>
|
||||
{
|
||||
var optionBuilder = new MqttClientOptions(serviceProvider);
|
||||
configureOptions(optionBuilder);
|
||||
return optionBuilder.Build();
|
||||
});
|
||||
|
||||
services.AddClientServiceWithProvider<MqttClientService>();
|
||||
return services;
|
||||
}
|
||||
|
||||
private static IServiceCollection AddRabbitMqClientServiceWithConfig(this IServiceCollection services, RabbitMqClientOptions options)
|
||||
{
|
||||
services.AddSingleton(options);
|
||||
|
||||
services.AddClientServiceWithProvider<RabbitMqClientService>();
|
||||
return services;
|
||||
}
|
||||
|
||||
private static IServiceCollection AddClientServiceWithProvider<T>(this IServiceCollection services) where T : class, ICommunicationService
|
||||
{
|
||||
services.AddSingleton<T>();
|
||||
services.AddSingleton<IHostedService>(serviceProvider =>
|
||||
{
|
||||
return serviceProvider.GetService<T>();
|
||||
});
|
||||
services.AddSingleton<ICommunicationServiceProvider>(serviceProvider =>
|
||||
{
|
||||
var clientService = serviceProvider.GetService<T>();
|
||||
var clientServiceProvider = new CommunicationServiceProvider(clientService);
|
||||
return clientServiceProvider;
|
||||
});
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -1,17 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.9" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.9" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.9">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="3.1.9" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -22,10 +22,17 @@ namespace Birdmap.DAL
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
EnsureCreated();
|
||||
AddDefaultUsers();
|
||||
AddDefaultServices();
|
||||
}
|
||||
|
||||
private void EnsureCreated()
|
||||
{
|
||||
_logger.LogInformation("Ensuring database is created...");
|
||||
_context.Database.EnsureCreated();
|
||||
}
|
||||
|
||||
private void AddDefaultServices()
|
||||
{
|
||||
_logger.LogInformation("Removing previously added default services...");
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Birdmap.DAL.Entities
|
||||
{
|
||||
public class Service
|
||||
public record Service
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
@ -6,7 +6,7 @@
|
||||
Admin,
|
||||
}
|
||||
|
||||
public class User
|
||||
public record User
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
14
Birdmap.sln
14
Birdmap.sln
@ -9,7 +9,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Birdmap.BLL", "Birdmap.BLL\
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Birdmap.DAL", "Birdmap.DAL\Birdmap.DAL.csproj", "{543FAB06-B960-41A9-8865-1624A2ED2170}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Birdmap.Common", "Birdmap.Common\Birdmap.Common.csproj", "{CE96BAFA-A0FD-4010-8EF2-700451091F71}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Birdmap.Common", "Birdmap.Common\Birdmap.Common.csproj", "{CE96BAFA-A0FD-4010-8EF2-700451091F71}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp.WinForm", "MQTTnet.TestApp.WinForm\MQTTnet.TestApp.WinForm.csproj", "{E1707FE7-4A65-42AC-B71C-6CC1A55FC42A}"
|
||||
EndProject
|
||||
Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{9443433B-1D13-41F0-B345-B36ACD15EF81}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@ -33,6 +37,14 @@ Global
|
||||
{CE96BAFA-A0FD-4010-8EF2-700451091F71}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CE96BAFA-A0FD-4010-8EF2-700451091F71}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CE96BAFA-A0FD-4010-8EF2-700451091F71}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E1707FE7-4A65-42AC-B71C-6CC1A55FC42A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E1707FE7-4A65-42AC-B71C-6CC1A55FC42A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E1707FE7-4A65-42AC-B71C-6CC1A55FC42A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E1707FE7-4A65-42AC-B71C-6CC1A55FC42A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9443433B-1D13-41F0-B345-B36ACD15EF81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9443433B-1D13-41F0-B345-B36ACD15EF81}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9443433B-1D13-41F0-B345-B36ACD15EF81}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9443433B-1D13-41F0-B345-B36ACD15EF81}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
27
Dockerfile
Normal file
27
Dockerfile
Normal file
@ -0,0 +1,27 @@
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
|
||||
WORKDIR /app
|
||||
RUN apt-get update && apt-get install -y curl
|
||||
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash -
|
||||
RUN apt-get update && apt-get install -y nodejs
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
|
||||
RUN apt-get update && apt-get install -y curl
|
||||
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash -
|
||||
RUN apt-get update && apt-get install -y nodejs
|
||||
WORKDIR /src
|
||||
COPY ["Birdmap.API/Birdmap.API.csproj", "Birdmap.API/"]
|
||||
COPY ["Birdmap.BLL/Birdmap.BLL.csproj", "Birdmap.BLL/"]
|
||||
COPY ["Birdmap.Common/Birdmap.Common.csproj", "Birdmap.Common/"]
|
||||
COPY ["Birdmap.DAL/Birdmap.DAL.csproj", "Birdmap.DAL/"]
|
||||
RUN dotnet restore "Birdmap.API/Birdmap.API.csproj"
|
||||
COPY . .
|
||||
WORKDIR "/src/Birdmap.API"
|
||||
RUN dotnet build "Birdmap.API.csproj" -c Release -o /app/build
|
||||
|
||||
FROM build AS publish
|
||||
RUN dotnet publish "Birdmap.API.csproj" -c Release -o /app/publish
|
||||
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "Birdmap.API.dll"]
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user