73 Commits

Author SHA1 Message Date
37ea775d59 And now for the final touches 0.2 2020-12-08 21:16:58 +01:00
c9c631b947 And now for the final touches 0.1 2020-12-08 21:15:01 +01:00
433b235929 I can't believe I have a (semi) finished version 2020-12-08 21:12:28 +01:00
7588b58453 My brain is fried 2020-12-07 23:19:09 +01:00
251a00eaa6 Removed appendices 2020-12-06 18:06:28 +01:00
5768b78619 Added last few chapter titles 2020-12-06 18:05:21 +01:00
08df5a624b Deleted unnecessary files 2020-12-06 18:03:09 +01:00
c0ae0a30fe Finished frontend description 2020-12-06 18:02:22 +01:00
8970d4fec3 Added frontend description 2020-12-06 15:41:59 +01:00
dbad96be95 Deleted .aux files 2020-12-04 09:31:42 +01:00
8764e1b45a Made system info requests multi threaded 2020-12-04 09:25:48 +01:00
6e61fc7756 Moved all services from API to BLL project 2020-12-03 18:52:25 +01:00
f0af8f08e3 Added API layer description 2020-12-03 16:37:13 +01:00
76c3787107 Added API layer logging 2020-12-02 21:41:01 +01:00
544df78ac8 Added Birdnetes models' 2020-12-01 16:27:26 +01:00
02df37692f Added DAL and BLL descriptions 2020-12-01 14:21:26 +01:00
ed4e207ff0 Finished birdmap-technologies 2020-12-01 09:44:11 +01:00
a5a37fdd1b Added birdmap-technologies 2020-11-30 16:34:08 +01:00
1b52e16db5 Added birdmap-technologies part 2020-11-28 17:38:33 +01:00
c9550ea5b8 Modified build batch file, and some formatting 2020-11-27 17:47:55 +01:00
3de33014e5 Added more content files 2020-11-27 16:44:16 +01:00
a458ad1712 Added birdnetes introduction 2020-11-27 15:37:25 +01:00
030b259d32 Added introduction 0.1 2020-11-26 19:44:53 +01:00
022852b163 Added abtract 0.4 2020-11-26 17:33:49 +01:00
2d5eca233e Added abtract 0.3 2020-11-26 16:28:33 +01:00
9d6e0cd453 Added abtract 0.2 2020-11-26 16:24:02 +01:00
a2c3112f81 Added abtract 0.1 2020-11-26 15:35:03 +01:00
c7e3fcabcf Added thesis base, and ideas.txt 2020-11-26 12:15:43 +01:00
70c4c91035 Fixed LogService baseUrl 2020-11-26 10:11:49 +01:00
aa39597541 Modified log folder path 2020-11-26 10:01:47 +01:00
bcbab1383a Added Logs navigation 2020-11-26 09:35:50 +01:00
bab0984c30 Added logdownloader 2020-11-25 14:10:47 +01:00
0667c6ec39 Added environment specific appsettings 2020-11-25 12:22:51 +01:00
0d71899ce1 Fixed docker-compose issues 2020-11-25 12:16:14 +01:00
966d8bd79e Some changes 2020-11-23 13:31:09 +01:00
5b42ce9f43 Multiple configuration modifications
Added Enviroment variable support
Addes baseUrl variable for live services
Included default env variables in docker-compose.yml
Updated sql and nodejs versions
2020-11-23 10:50:10 +01:00
85320d3cf3 Added dockerfile, added compose 2020-11-23 09:23:05 +01:00
9d55c39e33 Added Mqtt messages buffer to help unload frontend 2020-11-22 10:39:51 +01:00
d75e9d378d Added further optimazation 2020-11-22 09:56:32 +01:00
3cdaa2dc35 Increased timeout for ui update 2020-11-21 20:14:30 +01:00
9af0ba1bb8 Best optimization so far 2020-11-21 19:42:14 +01:00
04c27560ea Added timeout processing 2020-11-21 19:18:29 +01:00
73157520ab Optimized chart update (did not achieve much) 2020-11-21 18:32:12 +01:00
f862e4b8da Added all charts (pre optimization) 2020-11-21 18:10:36 +01:00
f85346aea9 Added charts 2020-11-21 17:40:05 +01:00
1d438bc349 Modified dashboard 2020-11-21 10:38:52 +01:00
86999cd646 Moved service components to services folder 2020-11-21 10:22:11 +01:00
8979ad6db3 Added dashboard services
Added GetCount endpoint
Added ServiceInfo Skeletons
2020-11-19 20:43:01 +01:00
779e21909c Added All Device handler 2020-11-18 18:34:48 +01:00
f84ea8f0c5 Extended Devices with buttons and status 2020-11-18 14:04:57 +01:00
181985859e Added componentDidMount heatmap initializer 2020-11-18 12:35:33 +01:00
39a38fe8eb Added context to heatmap, and devices 2020-11-18 12:04:32 +01:00
490f0f3265 Added context 2020-11-18 09:56:30 +01:00
41bf14a4e5 Merge branch 'master' into feature/Contexts 2020-11-18 09:19:05 +01:00
3f267cb009 Added background color, Added Accordion onChange handler 2020-11-17 22:20:50 +01:00
0e3eb8720f Added Devices Accordions 2020-11-17 21:51:53 +01:00
3f2467f6c6 Added Device Context (not working) 2020-11-17 18:58:28 +01:00
2a83856622 Removed exact from NavLink to fix highlighting 2020-11-13 19:11:25 +01:00
d726273431 Added position offset, changed color 2020-11-12 22:11:42 +01:00
412647617b Added markers with device openers 2020-11-12 21:20:04 +01:00
53ff60ae5a Added devices id route parameter 2020-11-12 18:57:27 +01:00
f273823c93 Migrated to .NET 5, added records 2020-11-12 18:13:23 +01:00
c92808ac7d Added single update 2020-11-11 20:56:21 +01:00
f13133829a Extended DeviceHub with update notifiers 2020-11-11 20:35:26 +01:00
4c1258dc33 Added Trello docs 2020-11-11 17:13:13 +01:00
a52f6acd71 Added google maps heatmap 2020-11-11 16:55:50 +01:00
4281c2d524 Added isAdmin 2020-11-09 18:30:43 +01:00
639e6edac6 Moved interface 2020-11-09 18:13:02 +01:00
3632e56dc4 Added Mqtt and SignalR 2020-11-09 18:12:07 +01:00
b87d90e5a4 Preparing mqtt 2020-11-08 23:29:50 +01:00
f1c1ad69cc Renamed some files 2020-11-08 19:11:12 +01:00
f102b89a21 Added MQTT tester, added input service 2020-11-08 18:51:19 +01:00
e1a596dae9 Updated csproj 2020-11-07 14:20:58 +01:00
173 changed files with 43027 additions and 1599 deletions

25
.dockerignore Normal file
View 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

View File

@ -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>

View File

@ -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",

View File

@ -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"

View File

@ -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.

View File

@ -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,140 +41,96 @@ 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} />
</Switch>
</BrowserRouter>
<BrowserRouter>
<Switch>
<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>
);
}
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',
},
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',
}
header: {
height: '7%',
},
body: {
backgroundColor: theme.palette.primary.dark,
height: '93%',
}
}),
);

View File

@ -0,0 +1,5 @@
export default {
probability_method_name: 'NotifyMessagesAsync',
update_method_name: 'NotifyDeviceUpdatedAsync',
update_all_method_name: 'NotifyAllUpdatedAsync',
};

View File

@ -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) {

File diff suppressed because one or more lines are too long

View File

@ -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')
}
};

View File

@ -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

View File

@ -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"}

View File

@ -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;

View File

@ -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

View File

@ -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"}

View File

@ -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
};

View 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',
}
},
}),
);

View File

@ -54,15 +54,15 @@ export default function Auth(props: any) {
setIsLoggingIn(true);
AuthService.login(username, password)
.then(() => {
props.onAuthenticated();
history.push('/');
}).catch(() => {
setShowError(true);
setErrorMessage('Invalid credentials');
}).finally(() => {
setIsLoggingIn(false);
});
.then(() => {
setIsLoggingIn(false);
props.onAuthenticated();
history.push('/');
}).catch(() => {
setShowError(true);
setIsLoggingIn(false);
setErrorMessage('Invalid credentials');
});
};
const renderErrorLabel = () => {

View 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

View 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);
}

View File

@ -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();
});
}

View File

@ -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"}

View File

@ -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();
});
}

View 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);

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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>
);
}

View File

@ -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>
);
}

View File

@ -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);

View File

@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View 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

View 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);

View File

@ -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);

View 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);

View File

@ -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

View File

@ -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 };

View File

@ -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'
});

View 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

View 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);
}

View 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);

View File

@ -0,0 +1,12 @@
import React from 'react';
export default React.createContext({
devices: [],
heatmapPoints: [],
addHandler: (_, __) => { },
removeHandler: (_, __) => { },
updateDevice: () => { },
updateAllDevices: () => { },
});

View 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>
);
};
}

View File

@ -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;

View File

@ -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();
}

View 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");
});
}
}
}

View File

@ -1,63 +1,99 @@
using AutoMapper;
using Birdmap.API.DTOs;
using Birdmap.BLL.Interfaces;
using Birdmap.BLL.Services.CommunicationServices.Hubs;
using Birdmap.BLL.Services.CommunicationServices.Mqtt;
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 IMqttClientService _mqttClientService;
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, MqttClientServiceProvider mqttClientProvider,
IHubContext<ServicesHub, IServicesHubClient> hubContext, ILogger<ServicesController> logger)
{
_service = service;
_mapper = mapper;
_mqttClientService = mqttClientProvider.MqttClientService;
_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)
{
try
tasks.Add(Task.Run(async () =>
{
_logger.LogInformation($"Sending a request to service [{si.Service.Name}] with url [{si.Service.Uri}]...");
var response = await client.GetAsync(si.Service.Uri);
si.StatusCode = response.StatusCode;
si.Response = await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
_logger.LogWarning($"Requesting service [{si.Service.Name}] faulted.");
si.StatusCode = System.Net.HttpStatusCode.ServiceUnavailable;
si.Response = ex.ToString();
}
var client = new HttpClient();
try
{
_logger.LogInformation($"Sending a request to service [{si.Service.Name}] with url [{si.Service.Uri}]...");
var response = await client.GetAsync(si.Service.Uri);
si.StatusCode = response.StatusCode;
si.Response = await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
_logger.LogWarning($"Requesting service [{si.Service.Name}] faulted.");
si.StatusCode = HttpStatusCode.ServiceUnavailable;
si.Response = ex.ToString();
}
}));
}
await Task.WhenAll(tasks);
serviceInfos.Add(new()
{
Service = new()
{
Id = 0,
Name = "Mqtt Client Service",
Uri = "localhost",
},
Response = $"IsConnected: {_mqttClientService.IsConnected}",
StatusCode = _mqttClientService.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 +102,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 +118,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();
}

View File

@ -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; }

View File

@ -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; }

View File

@ -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; }

View File

@ -2,7 +2,7 @@
namespace Birdmap.API.DTOs
{
public class ServiceInfo
public record ServiceInfo
{
public ServiceRequest Service { get; set; }
public HttpStatusCode StatusCode { get; set; }

View File

@ -1,6 +1,6 @@
namespace Birdmap.API.DTOs
{
public class ServiceRequest
public record ServiceRequest
{
public int Id { get; set; }
public string Name { get; set; }

27
Birdmap.API/Dockerfile Normal file
View 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"]

View File

@ -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();
}
}

View File

@ -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
}
}
}
}

View File

@ -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,10 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using NSwag.Generation.Processors.Security;
using System.Text;
namespace Birdmap
namespace Birdmap.API
{
public class Startup
{
@ -48,7 +49,7 @@ namespace Birdmap
})
.AddJwtBearer(opt =>
{
// opt.RequireHttpsMetadata = false;
//opt.RequireHttpsMetadata = false;
opt.SaveToken = true;
opt.IncludeErrorDetails = true;
opt.TokenValidationParameters = new TokenValidationParameters
@ -65,6 +66,20 @@ namespace Birdmap
{
configuration.RootPath = "ClientApp/build";
});
services.AddSwaggerDocument(opt =>
{
opt.Title = "Birdmap";
opt.OperationProcessors.Add(new OperationSecurityScopeProcessor("Jwt Token"));
opt.AddSecurity("Jwt Token", new 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 +89,27 @@ namespace Birdmap
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseMiddleware<ExceptionHandlerMiddleware>();
}
app.UseMiddleware<ExceptionHandlerMiddleware>();
app.UseOpenApi();
app.UseSwaggerUi3();
app.UseHttpsRedirection();
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 =>

View File

@ -5,5 +5,50 @@
"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/",
"Mqtt": {
"BrokerHostSettings": {
"Host": "localhost",
"Port": 1883
},
"ClientSettings": {
"Id": "ASP.NET Core client",
"Username": "username",
"Password": "password",
"Topic": "devices/output"
}
}
}

View File

@ -6,27 +6,35 @@
"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"
},
{
"Name": "user",
"Password": "pass",
"Role": "User"
}
]
"Users": []
},
"UseDummyServices": true
"UseDummyServices": false,
"ServicesBaseUrl": "https://birb.k8s.kmlabz.com/",
"Mqtt": {
"BrokerHostSettings": {
"Host": "",
"Port": 1883
},
"ClientSettings": {
"Id": "ASP.NET Core client",
"Username": "",
"Password": "",
"Topic": ""
}
}
}

5
Birdmap.API/libman.json Normal file
View File

@ -0,0 +1,5 @@
{
"version": "1.0",
"defaultProvider": "cdnjs",
"libraries": []
}

View File

@ -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,34 @@
<!-- 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"/>
<!--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>

View File

@ -1,9 +1,16 @@
<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" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Birdmap.Common\Birdmap.Common.csproj" />
<ProjectReference Include="..\Birdmap.DAL\Birdmap.DAL.csproj" />

View 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()));
}
}
}

View 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; }
}
}

View File

@ -0,0 +1,15 @@
using Microsoft.Extensions.Hosting;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Receiving;
namespace Birdmap.BLL.Interfaces
{
public interface IMqttClientService : IHostedService,
IMqttClientConnectedHandler,
IMqttClientDisconnectedHandler,
IMqttApplicationMessageReceivedHandler
{
public bool IsConnected { get; }
}
}

View File

@ -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);

View File

@ -0,0 +1,22 @@
using MQTTnet.Client.Options;
using System;
namespace Birdmap.BLL.Options
{
public class AspCoreMqttClientOptions : MqttClientOptionsBuilder
{
public IServiceProvider ServiceProvider { get; }
public AspCoreMqttClientOptions(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public AspCoreMqttClientOptions WithTopic(string topic)
{
WithUserProperty("Topic", topic);
return this;
}
}
}

View 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);
}
}
}

View File

@ -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();
}
}

View File

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Birdmap.BLL.Services.CommunicationServices.Hubs
{
public interface IServicesHubClient
{
Task NotifyUpdatedAsync();
}
}

View 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);
}
}
}

View File

@ -0,0 +1,172 @@
using Birdmap.BLL.Interfaces;
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 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.CommunicationServices.Mqtt
{
public class MqttClientService : IMqttClientService
{
private readonly IMqttClient _mqttClient;
private readonly IMqttClientOptions _options;
private readonly ILogger<MqttClientService> _logger;
private readonly IInputService _inputService;
private readonly IHubContext<DevicesHub, IDevicesHubClient> _hubContext;
private readonly Timer _hubTimer;
private readonly List<Message> _messages = new();
private readonly object _messageLock = new();
public bool IsConnected => _mqttClient.IsConnected;
public MqttClientService(IMqttClientOptions options, ILogger<MqttClientService> logger, IInputService inputService, IHubContext<DevicesHub, IDevicesHubClient> hubContext)
{
_options = options;
_logger = logger;
_inputService = inputService;
_hubContext = hubContext;
_hubTimer = new Timer()
{
AutoReset = true,
Interval = 1000,
};
_hubTimer.Elapsed += SendMqttMessagesWithSignalR;
_mqttClient = new MqttFactory().CreateMqttClient();
ConfigureMqttClient();
}
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();
}
}
}
private void ConfigureMqttClient()
{
_mqttClient.ConnectedHandler = this;
_mqttClient.DisconnectedHandler = this;
_mqttClient.ApplicationMessageReceivedHandler = this;
}
private class Payload
{
[JsonProperty("tag")]
public Guid TagID { get; set; }
[JsonProperty("probability")]
public double Probability { get; set; }
}
public async 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}");
try
{
var payload = JsonConvert.DeserializeObject<Payload>(message);
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.");
}
}
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);
_hubTimer.Start();
}
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
{
_hubTimer.Stop();
await _mqttClient.ConnectAsync(_options, CancellationToken.None);
}
catch (Exception ex)
{
_logger.LogDebug(ex, $"Reconnect failed...");
}
}
public 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 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...");
}
}
}
}

View File

@ -0,0 +1,14 @@
using Birdmap.BLL.Interfaces;
namespace Birdmap.BLL.Services.CommunicationServices.Mqtt
{
public class MqttClientServiceProvider
{
public IMqttClientService MqttClientService { get; }
public MqttClientServiceProvider(IMqttClientService mqttClientService)
{
MqttClientService = mqttClientService;
}
}
}

View File

@ -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);

View File

@ -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);
}
}
}
}

View File

@ -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

View 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

View File

@ -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);

View File

@ -1,7 +1,12 @@
using Birdmap.BLL.Interfaces;
using Birdmap.BLL.Options;
using Birdmap.BLL.Services;
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 +19,79 @@ 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();
services.AddMqttClientServiceWithConfig(opt =>
{
var mqtt = configuration.GetSection("Mqtt");
var mqttClient = mqtt.GetSection("ClientSettings");
var clientSettings = new
{
Id = mqttClient.GetValue<string>("Id"),
Username = mqttClient.GetValue<string>("Username"),
Password = mqttClient.GetValue<string>("Password"),
Topic = mqttClient.GetValue<string>("Topic"),
};
var mqttBrokerHost = mqtt.GetSection("BrokerHostSettings");
var brokerHostSettings = new
{
Host = mqttBrokerHost.GetValue<string>("Host"),
Port = mqttBrokerHost.GetValue<int>("Port"),
};
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<AspCoreMqttClientOptions> configureOptions)
{
services.AddSingleton(serviceProvider =>
{
var optionBuilder = new AspCoreMqttClientOptions(serviceProvider);
configureOptions(optionBuilder);
return optionBuilder.Build();
});
services.AddSingleton<MqttClientService>();
services.AddSingleton<IHostedService>(serviceProvider =>
{
return serviceProvider.GetService<MqttClientService>();
});
services.AddSingleton(serviceProvider =>
{
var mqttClientService = serviceProvider.GetService<MqttClientService>();
var mqttClientServiceProvider = new MqttClientServiceProvider(mqttClientService);
return mqttClientServiceProvider;
});
return services;
}
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
</Project>

View File

@ -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>

View File

@ -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...");

View File

@ -2,7 +2,7 @@
namespace Birdmap.DAL.Entities
{
public class Service
public record Service
{
public int Id { get; set; }
public string Name { get; set; }

View File

@ -6,7 +6,7 @@
Admin,
}
public class User
public record User
{
public int Id { get; set; }
public string Name { get; set; }

View File

@ -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

394
MQTTnet.TestApp.WinForm/Form1.Designer.cs generated Normal file
View File

@ -0,0 +1,394 @@
namespace MQTTnet.TestApp.WinForm
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.TextBoxPort = new System.Windows.Forms.TextBox();
this.ButtonServerStart = new System.Windows.Forms.Button();
this.ButtonServerStop = new System.Windows.Forms.Button();
this.label2 = new System.Windows.Forms.Label();
this.maskedTextBox1 = new System.Windows.Forms.MaskedTextBox();
this.label3 = new System.Windows.Forms.Label();
this.TextBoxTopicPublished = new System.Windows.Forms.TextBox();
this.label4 = new System.Windows.Forms.Label();
this.label5 = new System.Windows.Forms.Label();
this.ButtonPublisherStop = new System.Windows.Forms.Button();
this.ButtonPublisherStart = new System.Windows.Forms.Button();
this.TextBoxPublish = new System.Windows.Forms.TextBox();
this.ButtonPublish = new System.Windows.Forms.Button();
this.ButtonSubscriberStop = new System.Windows.Forms.Button();
this.ButtonSubscriberStart = new System.Windows.Forms.Button();
this.TextBoxSubscriber = new System.Windows.Forms.TextBox();
this.ButtonGeneratePublishedMessage = new System.Windows.Forms.Button();
this.label6 = new System.Windows.Forms.Label();
this.TextBoxTopicSubscribed = new System.Windows.Forms.TextBox();
this.ButtonSubscribe = new System.Windows.Forms.Button();
this.label7 = new System.Windows.Forms.Label();
this.trackBar1 = new System.Windows.Forms.TrackBar();
this.ButtonAutoPublisherStop = new System.Windows.Forms.Button();
this.ButtonAutoPublisherStart = new System.Windows.Forms.Button();
this.label8 = new System.Windows.Forms.Label();
this.checkBox1 = new System.Windows.Forms.CheckBox();
((System.ComponentModel.ISupportInitialize)(this.trackBar1)).BeginInit();
this.SuspendLayout();
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(119, 24);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(32, 15);
this.label1.TabIndex = 0;
this.label1.Text = "Port:";
//
// TextBoxPort
//
this.TextBoxPort.Location = new System.Drawing.Point(172, 21);
this.TextBoxPort.Name = "TextBoxPort";
this.TextBoxPort.Size = new System.Drawing.Size(100, 23);
this.TextBoxPort.TabIndex = 1;
this.TextBoxPort.Text = "1883";
this.TextBoxPort.TextChanged += new System.EventHandler(this.TextBoxPortTextChanged);
//
// ButtonServerStart
//
this.ButtonServerStart.Location = new System.Drawing.Point(433, 21);
this.ButtonServerStart.Name = "ButtonServerStart";
this.ButtonServerStart.Size = new System.Drawing.Size(75, 23);
this.ButtonServerStart.TabIndex = 2;
this.ButtonServerStart.Text = "Start";
this.ButtonServerStart.UseVisualStyleBackColor = true;
this.ButtonServerStart.Click += new System.EventHandler(this.ButtonServerStartClick);
//
// ButtonServerStop
//
this.ButtonServerStop.Location = new System.Drawing.Point(514, 21);
this.ButtonServerStop.Name = "ButtonServerStop";
this.ButtonServerStop.Size = new System.Drawing.Size(75, 23);
this.ButtonServerStop.TabIndex = 3;
this.ButtonServerStop.Text = "Stop";
this.ButtonServerStop.UseVisualStyleBackColor = true;
this.ButtonServerStop.Click += new System.EventHandler(this.ButtonServerStopClick);
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(33, 25);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(39, 15);
this.label2.TabIndex = 4;
this.label2.Text = "Server";
//
// maskedTextBox1
//
this.maskedTextBox1.Location = new System.Drawing.Point(0, 0);
this.maskedTextBox1.Name = "maskedTextBox1";
this.maskedTextBox1.Size = new System.Drawing.Size(100, 23);
this.maskedTextBox1.TabIndex = 5;
this.maskedTextBox1.Text = "maskedTextBox1";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(33, 79);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(90, 15);
this.label3.TabIndex = 5;
this.label3.Text = "Topic Published";
//
// TextBoxTopicPublished
//
this.TextBoxTopicPublished.Location = new System.Drawing.Point(164, 76);
this.TextBoxTopicPublished.Name = "TextBoxTopicPublished";
this.TextBoxTopicPublished.Size = new System.Drawing.Size(425, 23);
this.TextBoxTopicPublished.TabIndex = 1;
this.TextBoxTopicPublished.Text = "devices/output";
this.TextBoxTopicPublished.TextChanged += new System.EventHandler(this.TextBoxPortTextChanged);
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(33, 109);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(90, 15);
this.label4.TabIndex = 5;
this.label4.Text = "Client Publisher";
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(33, 224);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(96, 15);
this.label5.TabIndex = 5;
this.label5.Text = "Client Subscriber";
//
// ButtonPublisherStop
//
this.ButtonPublisherStop.Location = new System.Drawing.Point(514, 105);
this.ButtonPublisherStop.Name = "ButtonPublisherStop";
this.ButtonPublisherStop.Size = new System.Drawing.Size(75, 23);
this.ButtonPublisherStop.TabIndex = 3;
this.ButtonPublisherStop.Text = "Stop";
this.ButtonPublisherStop.UseVisualStyleBackColor = true;
this.ButtonPublisherStop.Click += new System.EventHandler(this.ButtonPublisherStopClick);
//
// ButtonPublisherStart
//
this.ButtonPublisherStart.Location = new System.Drawing.Point(433, 105);
this.ButtonPublisherStart.Name = "ButtonPublisherStart";
this.ButtonPublisherStart.Size = new System.Drawing.Size(75, 23);
this.ButtonPublisherStart.TabIndex = 2;
this.ButtonPublisherStart.Text = "Start";
this.ButtonPublisherStart.UseVisualStyleBackColor = true;
this.ButtonPublisherStart.Click += new System.EventHandler(this.ButtonPublisherStartClick);
//
// TextBoxPublish
//
this.TextBoxPublish.Location = new System.Drawing.Point(33, 134);
this.TextBoxPublish.Name = "TextBoxPublish";
this.TextBoxPublish.Size = new System.Drawing.Size(394, 23);
this.TextBoxPublish.TabIndex = 1;
this.TextBoxPublish.TextChanged += new System.EventHandler(this.TextBoxPortTextChanged);
//
// ButtonPublish
//
this.ButtonPublish.Location = new System.Drawing.Point(514, 134);
this.ButtonPublish.Name = "ButtonPublish";
this.ButtonPublish.Size = new System.Drawing.Size(75, 23);
this.ButtonPublish.TabIndex = 3;
this.ButtonPublish.Text = "Publish";
this.ButtonPublish.UseVisualStyleBackColor = true;
this.ButtonPublish.Click += new System.EventHandler(this.ButtonPublishClick);
//
// ButtonSubscriberStop
//
this.ButtonSubscriberStop.Location = new System.Drawing.Point(514, 220);
this.ButtonSubscriberStop.Name = "ButtonSubscriberStop";
this.ButtonSubscriberStop.Size = new System.Drawing.Size(75, 23);
this.ButtonSubscriberStop.TabIndex = 3;
this.ButtonSubscriberStop.Text = "Stop";
this.ButtonSubscriberStop.UseVisualStyleBackColor = true;
this.ButtonSubscriberStop.Click += new System.EventHandler(this.ButtonSubscriberStopClick);
//
// ButtonSubscriberStart
//
this.ButtonSubscriberStart.Location = new System.Drawing.Point(433, 220);
this.ButtonSubscriberStart.Name = "ButtonSubscriberStart";
this.ButtonSubscriberStart.Size = new System.Drawing.Size(75, 23);
this.ButtonSubscriberStart.TabIndex = 2;
this.ButtonSubscriberStart.Text = "Start";
this.ButtonSubscriberStart.UseVisualStyleBackColor = true;
this.ButtonSubscriberStart.Click += new System.EventHandler(this.ButtonSubscriberStartClick);
//
// TextBoxSubscriber
//
this.TextBoxSubscriber.Location = new System.Drawing.Point(33, 309);
this.TextBoxSubscriber.Multiline = true;
this.TextBoxSubscriber.Name = "TextBoxSubscriber";
this.TextBoxSubscriber.Size = new System.Drawing.Size(556, 182);
this.TextBoxSubscriber.TabIndex = 6;
//
// ButtonGeneratePublishedMessage
//
this.ButtonGeneratePublishedMessage.Location = new System.Drawing.Point(433, 133);
this.ButtonGeneratePublishedMessage.Name = "ButtonGeneratePublishedMessage";
this.ButtonGeneratePublishedMessage.Size = new System.Drawing.Size(75, 23);
this.ButtonGeneratePublishedMessage.TabIndex = 2;
this.ButtonGeneratePublishedMessage.Text = "Random";
this.ButtonGeneratePublishedMessage.UseVisualStyleBackColor = true;
this.ButtonGeneratePublishedMessage.Click += new System.EventHandler(this.ButtonGeneratePublishedMessageClick);
//
// label6
//
this.label6.AutoSize = true;
this.label6.Location = new System.Drawing.Point(33, 252);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(96, 15);
this.label6.TabIndex = 5;
this.label6.Text = "Topic Subscribed";
//
// TextBoxTopicSubscribed
//
this.TextBoxTopicSubscribed.Location = new System.Drawing.Point(164, 249);
this.TextBoxTopicSubscribed.Name = "TextBoxTopicSubscribed";
this.TextBoxTopicSubscribed.Size = new System.Drawing.Size(344, 23);
this.TextBoxTopicSubscribed.TabIndex = 1;
this.TextBoxTopicSubscribed.Text = "devices/output";
this.TextBoxTopicSubscribed.TextChanged += new System.EventHandler(this.TextBoxPortTextChanged);
//
// ButtonSubscribe
//
this.ButtonSubscribe.Location = new System.Drawing.Point(514, 248);
this.ButtonSubscribe.Name = "ButtonSubscribe";
this.ButtonSubscribe.Size = new System.Drawing.Size(75, 23);
this.ButtonSubscribe.TabIndex = 3;
this.ButtonSubscribe.Text = "Subscribe";
this.ButtonSubscribe.UseVisualStyleBackColor = true;
this.ButtonSubscribe.Click += new System.EventHandler(this.ButtonSubscribeClick);
//
// label7
//
this.label7.AutoSize = true;
this.label7.Location = new System.Drawing.Point(33, 167);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(141, 15);
this.label7.TabIndex = 5;
this.label7.Text = "Auto (Random) Publisher";
//
// trackBar1
//
this.trackBar1.LargeChange = 500;
this.trackBar1.Location = new System.Drawing.Point(180, 162);
this.trackBar1.Maximum = 5050;
this.trackBar1.Minimum = 50;
this.trackBar1.Name = "trackBar1";
this.trackBar1.Size = new System.Drawing.Size(247, 45);
this.trackBar1.SmallChange = 100;
this.trackBar1.TabIndex = 7;
this.trackBar1.TickFrequency = 500;
this.trackBar1.Value = 550;
this.trackBar1.Scroll += new System.EventHandler(this.trackBar1_Scroll);
//
// ButtonAutoPublisherStop
//
this.ButtonAutoPublisherStop.Location = new System.Drawing.Point(513, 162);
this.ButtonAutoPublisherStop.Name = "ButtonAutoPublisherStop";
this.ButtonAutoPublisherStop.Size = new System.Drawing.Size(75, 23);
this.ButtonAutoPublisherStop.TabIndex = 8;
this.ButtonAutoPublisherStop.Text = "Stop";
this.ButtonAutoPublisherStop.UseVisualStyleBackColor = true;
this.ButtonAutoPublisherStop.Click += new System.EventHandler(this.ButtonAutoPublisherStopClick);
//
// ButtonAutoPublisherStart
//
this.ButtonAutoPublisherStart.Location = new System.Drawing.Point(433, 162);
this.ButtonAutoPublisherStart.Name = "ButtonAutoPublisherStart";
this.ButtonAutoPublisherStart.Size = new System.Drawing.Size(75, 23);
this.ButtonAutoPublisherStart.TabIndex = 8;
this.ButtonAutoPublisherStart.Text = "Start";
this.ButtonAutoPublisherStart.UseVisualStyleBackColor = true;
this.ButtonAutoPublisherStart.Click += new System.EventHandler(this.ButtonAutoPublisherStartClick);
//
// label8
//
this.label8.Anchor = System.Windows.Forms.AnchorStyles.Right;
this.label8.AutoSize = true;
this.label8.Location = new System.Drawing.Point(180, 191);
this.label8.Name = "label8";
this.label8.RightToLeft = System.Windows.Forms.RightToLeft.No;
this.label8.Size = new System.Drawing.Size(44, 15);
this.label8.TabIndex = 9;
this.label8.Text = "550 ms";
this.label8.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// checkBox1
//
this.checkBox1.AutoSize = true;
this.checkBox1.Checked = true;
this.checkBox1.CheckState = System.Windows.Forms.CheckState.Checked;
this.checkBox1.Location = new System.Drawing.Point(433, 191);
this.checkBox1.Name = "checkBox1";
this.checkBox1.Size = new System.Drawing.Size(82, 19);
this.checkBox1.TabIndex = 10;
this.checkBox1.Text = "randomize";
this.checkBox1.UseVisualStyleBackColor = true;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(627, 530);
this.Controls.Add(this.checkBox1);
this.Controls.Add(this.label8);
this.Controls.Add(this.ButtonAutoPublisherStart);
this.Controls.Add(this.ButtonAutoPublisherStop);
this.Controls.Add(this.trackBar1);
this.Controls.Add(this.label7);
this.Controls.Add(this.TextBoxSubscriber);
this.Controls.Add(this.label5);
this.Controls.Add(this.label4);
this.Controls.Add(this.label3);
this.Controls.Add(this.label2);
this.Controls.Add(this.ButtonServerStop);
this.Controls.Add(this.ButtonServerStart);
this.Controls.Add(this.TextBoxPort);
this.Controls.Add(this.label1);
this.Controls.Add(this.TextBoxTopicPublished);
this.Controls.Add(this.ButtonPublisherStop);
this.Controls.Add(this.ButtonPublisherStart);
this.Controls.Add(this.TextBoxPublish);
this.Controls.Add(this.ButtonPublish);
this.Controls.Add(this.ButtonSubscriberStop);
this.Controls.Add(this.ButtonSubscriberStart);
this.Controls.Add(this.ButtonGeneratePublishedMessage);
this.Controls.Add(this.label6);
this.Controls.Add(this.TextBoxTopicSubscribed);
this.Controls.Add(this.ButtonSubscribe);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Name = "Form1";
this.Text = "MQTT Testing";
((System.ComponentModel.ISupportInitialize)(this.trackBar1)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox TextBoxPort;
private System.Windows.Forms.Button ButtonServerStart;
private System.Windows.Forms.Button ButtonServerStop;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.MaskedTextBox maskedTextBox1;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.Button ButtonPublisherStop;
private System.Windows.Forms.Button ButtonPublisherStart;
private System.Windows.Forms.TextBox TextBoxPublish;
private System.Windows.Forms.Button ButtonPublish;
private System.Windows.Forms.Button ButtonSubscriberStop;
private System.Windows.Forms.Button ButtonSubscriberStart;
private System.Windows.Forms.TextBox TextBoxSubscriber;
private System.Windows.Forms.Button ButtonGeneratePublishedMessage;
private System.Windows.Forms.Label label6;
private System.Windows.Forms.TextBox TextBoxTopicPublished;
private System.Windows.Forms.Button ButtonSubscribe;
private System.Windows.Forms.TextBox TextBoxTopicSubscribed;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.TrackBar trackBar1;
private System.Windows.Forms.Button ButtonAutoPublisherStop;
private System.Windows.Forms.Button ButtonAutoPublisherStart;
private System.Windows.Forms.Label label8;
private System.Windows.Forms.CheckBox checkBox1;
}
}

View File

@ -0,0 +1,476 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Form1.cs" company="Haemmer Electronics">
// Copyright (c) 2020 All rights reserved.
// </copyright>
// <summary>
// The main form.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace MQTTnet.TestApp.WinForm
{
using System;
using System.Text;
using System.Timers;
using System.Windows.Forms;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Options;
using MQTTnet.Client.Receiving;
using MQTTnet.Extensions.ManagedClient;
using MQTTnet.Formatter;
using MQTTnet.Protocol;
using MQTTnet.Server;
using Newtonsoft.Json;
using Timer = System.Timers.Timer;
/// <summary>
/// The main form.
/// </summary>
public partial class Form1 : Form
{
/// <summary>
/// The managed publisher client.
/// </summary>
private IManagedMqttClient managedMqttClientPublisher;
/// <summary>
/// The managed subscriber client.
/// </summary>
private IManagedMqttClient managedMqttClientSubscriber;
/// <summary>
/// The MQTT server.
/// </summary>
private IMqttServer mqttServer;
/// <summary>
/// The port.
/// </summary>
private string port = "1883";
private readonly Random random;
private readonly Timer randomPublisherTimer;
/// <summary>
/// Initializes a new instance of the <see cref="Form1"/> class.
/// </summary>
public Form1()
{
this.InitializeComponent();
var timer = new Timer
{
AutoReset = true, Enabled = true, Interval = 1000
};
timer.Elapsed += this.TimerElapsed;
random = new Random();
randomPublisherTimer = new Timer
{
AutoReset = true,
Enabled = false,
};
randomPublisherTimer.Elapsed += (_, __) => this.BeginInvoke((MethodInvoker)delegate
{
if (checkBox1.Checked)
ButtonGeneratePublishedMessageClick(ButtonGeneratePublishedMessage, EventArgs.Empty);
ButtonPublishClick(ButtonPublish, EventArgs.Empty);
});
}
/// <summary>
/// Handles the publisher connected event.
/// </summary>
/// <param name="x">The MQTT client connected event args.</param>
private void OnPublisherConnected(MqttClientConnectedEventArgs x)
{
var item = "Publisher Connected";
this.BeginInvoke((MethodInvoker)delegate { this.TextBoxSubscriber.Text = item + Environment.NewLine + this.TextBoxSubscriber.Text; });
}
/// <summary>
/// Handles the publisher disconnected event.
/// </summary>
/// <param name="x">The MQTT client disconnected event args.</param>
private void OnPublisherDisconnected(MqttClientDisconnectedEventArgs x)
{
var item = "Publisher Disconnected";
this.BeginInvoke((MethodInvoker)delegate { this.TextBoxSubscriber.Text = item + Environment.NewLine + this.TextBoxSubscriber.Text; });
}
/// <summary>
/// Handles the subscriber connected event.
/// </summary>
/// <param name="x">The MQTT client connected event args.</param>
private void OnSubscriberConnected(MqttClientConnectedEventArgs x)
{
var item = "Subscriber Connected";
this.BeginInvoke((MethodInvoker)delegate { this.TextBoxSubscriber.Text = item + Environment.NewLine + this.TextBoxSubscriber.Text; });
}
/// <summary>
/// Handles the subscriber disconnected event.
/// </summary>
/// <param name="x">The MQTT client disconnected event args.</param>
private void OnSubscriberDisconnected(MqttClientDisconnectedEventArgs x)
{
var item = "Subscriber Disconnected";
this.BeginInvoke((MethodInvoker)delegate { this.TextBoxSubscriber.Text = item + Environment.NewLine + this.TextBoxSubscriber.Text; });
}
/// <summary>
/// The method that handles the button click to generate a message.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The event args.</param>
private void ButtonGeneratePublishedMessageClick(object sender, EventArgs e)
{
var message = new
{
tag = Guid.NewGuid(),
probability = random.NextDouble(),
};
var json = JsonConvert.SerializeObject(message);
this.TextBoxPublish.Text = json;
}
/// <summary>
/// The method that handles the button click to publish a message.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The event args.</param>
private async void ButtonPublishClick(object sender, EventArgs e)
{
((Button)sender).Enabled = false;
try
{
var payload = Encoding.UTF8.GetBytes(this.TextBoxPublish.Text);
var message = new MqttApplicationMessageBuilder().WithTopic(this.TextBoxTopicPublished.Text.Trim()).WithPayload(payload).WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce).WithRetainFlag().Build();
if (this.managedMqttClientPublisher != null)
{
await this.managedMqttClientPublisher.PublishAsync(message);
}
}
catch (Exception ex)
{
var item = ex.Message;
this.BeginInvoke((MethodInvoker)delegate { this.TextBoxSubscriber.Text = item + Environment.NewLine + this.TextBoxSubscriber.Text; });
}
((Button)sender).Enabled = true;
}
/// <summary>
/// The method that handles the button click to start the publisher.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The event args.</param>
private async void ButtonPublisherStartClick(object sender, EventArgs e)
{
var mqttFactory = new MqttFactory();
var tlsOptions = new MqttClientTlsOptions
{
UseTls = false, IgnoreCertificateChainErrors = true, IgnoreCertificateRevocationErrors = true, AllowUntrustedCertificates = true
};
var options = new MqttClientOptions
{
ClientId = "ClientPublisher",
ProtocolVersion = MqttProtocolVersion.V311,
ChannelOptions = new MqttClientTcpOptions
{
Server = "localhost", Port = int.Parse(this.TextBoxPort.Text.Trim()), TlsOptions = tlsOptions
}
};
if (options.ChannelOptions == null)
{
throw new InvalidOperationException();
}
options.Credentials = new MqttClientCredentials
{
Username = "username", Password = Encoding.UTF8.GetBytes("password")
};
options.CleanSession = true;
options.KeepAlivePeriod = TimeSpan.FromSeconds(5);
this.managedMqttClientPublisher = mqttFactory.CreateManagedMqttClient();
this.managedMqttClientPublisher.UseApplicationMessageReceivedHandler(this.HandleReceivedApplicationMessage);
this.managedMqttClientPublisher.ConnectedHandler = new MqttClientConnectedHandlerDelegate(OnPublisherConnected);
this.managedMqttClientPublisher.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(OnPublisherDisconnected);
await this.managedMqttClientPublisher.StartAsync(
new ManagedMqttClientOptions
{
ClientOptions = options
});
}
/// <summary>
/// The method that handles the button click to stop the publisher.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The event args.</param>
private async void ButtonPublisherStopClick(object sender, EventArgs e)
{
if (this.managedMqttClientPublisher == null)
{
return;
}
await this.managedMqttClientPublisher.StopAsync();
this.managedMqttClientPublisher = null;
}
/// <summary>
/// The method that handles the button click to start the server.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The event args.</param>
private async void ButtonServerStartClick(object sender, EventArgs e)
{
if (this.mqttServer != null)
{
return;
}
var storage = new JsonServerStorage();
storage.Clear();
this.mqttServer = new MqttFactory().CreateMqttServer();
var options = new MqttServerOptions();
options.DefaultEndpointOptions.Port = int.Parse(this.TextBoxPort.Text);
options.Storage = storage;
options.EnablePersistentSessions = true;
options.ConnectionValidator = new MqttServerConnectionValidatorDelegate(
c =>
{
if (c.ClientId.Length < 10)
{
c.ReasonCode = MqttConnectReasonCode.ClientIdentifierNotValid;
return;
}
if (c.Username != "username")
{
c.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
return;
}
if (c.Password != "password")
{
c.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
return;
}
c.ReasonCode = MqttConnectReasonCode.Success;
});
try
{
await this.mqttServer.StartAsync(options);
}
catch (Exception ex)
{
var item = ex.Message;
this.BeginInvoke((MethodInvoker)delegate { this.TextBoxSubscriber.Text = item + Environment.NewLine + this.TextBoxSubscriber.Text; });
await this.mqttServer.StopAsync();
this.mqttServer = null;
}
}
/// <summary>
/// The method that handles the button click to stop the server.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The event args.</param>
private async void ButtonServerStopClick(object sender, EventArgs e)
{
if (this.mqttServer == null)
{
return;
}
await this.mqttServer.StopAsync();
this.mqttServer = null;
}
/// <summary>
/// The method that handles the button click to subscribe to a certain topic.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The event args.</param>
private async void ButtonSubscribeClick(object sender, EventArgs e)
{
var topicFilter = new MqttTopicFilter { Topic = this.TextBoxTopicSubscribed.Text.Trim() };
await this.managedMqttClientSubscriber.SubscribeAsync(topicFilter);
var item = "Topic " + this.TextBoxTopicSubscribed.Text.Trim() + " is subscribed";
this.BeginInvoke((MethodInvoker)delegate { this.TextBoxSubscriber.Text = item + Environment.NewLine + this.TextBoxSubscriber.Text; });
}
/// <summary>
/// The method that handles the button click to start the subscriber.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The event args.</param>
private async void ButtonSubscriberStartClick(object sender, EventArgs e)
{
var mqttFactory = new MqttFactory();
var tlsOptions = new MqttClientTlsOptions
{
UseTls = false, IgnoreCertificateChainErrors = true, IgnoreCertificateRevocationErrors = true, AllowUntrustedCertificates = true
};
var options = new MqttClientOptions
{
ClientId = "ClientSubscriber",
ProtocolVersion = MqttProtocolVersion.V311,
ChannelOptions = new MqttClientTcpOptions
{
Server = "localhost", Port = int.Parse(this.TextBoxPort.Text.Trim()), TlsOptions = tlsOptions
}
};
if (options.ChannelOptions == null)
{
throw new InvalidOperationException();
}
options.Credentials = new MqttClientCredentials
{
Username = "username",
Password = Encoding.UTF8.GetBytes("password")
};
options.CleanSession = true;
options.KeepAlivePeriod = TimeSpan.FromSeconds(5);
this.managedMqttClientSubscriber = mqttFactory.CreateManagedMqttClient();
this.managedMqttClientSubscriber.ConnectedHandler = new MqttClientConnectedHandlerDelegate(OnSubscriberConnected);
this.managedMqttClientSubscriber.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(OnSubscriberDisconnected);
this.managedMqttClientSubscriber.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(this.OnSubscriberMessageReceived);
await this.managedMqttClientSubscriber.StartAsync(
new ManagedMqttClientOptions
{
ClientOptions = options
});
}
/// <summary>
/// The method that handles the button click to stop the subscriber.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The event args.</param>
private async void ButtonSubscriberStopClick(object sender, EventArgs e)
{
if (this.managedMqttClientSubscriber == null)
{
return;
}
await this.managedMqttClientSubscriber.StopAsync();
this.managedMqttClientSubscriber = null;
}
/// <summary>
/// Handles the received application message event.
/// </summary>
/// <param name="x">The MQTT application message received event args.</param>
private void HandleReceivedApplicationMessage(MqttApplicationMessageReceivedEventArgs x)
{
var item = $"Timestamp: {DateTime.Now:O} | Topic: {x.ApplicationMessage.Topic} | Payload: {x.ApplicationMessage.ConvertPayloadToString()} | QoS: {x.ApplicationMessage.QualityOfServiceLevel}";
this.BeginInvoke((MethodInvoker)delegate { this.TextBoxSubscriber.Text = item + Environment.NewLine + this.TextBoxSubscriber.Text; });
}
/// <summary>
/// Handles the received subscriber message event.
/// </summary>
/// <param name="x">The MQTT application message received event args.</param>
private void OnSubscriberMessageReceived(MqttApplicationMessageReceivedEventArgs x)
{
var item = $"Timestamp: {DateTime.Now:O} | Topic: {x.ApplicationMessage.Topic} | Payload: {x.ApplicationMessage.ConvertPayloadToString()} | QoS: {x.ApplicationMessage.QualityOfServiceLevel}";
this.BeginInvoke((MethodInvoker)delegate { this.TextBoxSubscriber.Text = item + Environment.NewLine + this.TextBoxSubscriber.Text; });
}
/// <summary>
/// The method that handles the text changes in the text box.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The event args.</param>
private void TextBoxPortTextChanged(object sender, EventArgs e)
{
// ReSharper disable once StyleCop.SA1126
if (int.TryParse(this.TextBoxPort.Text, out _))
{
this.port = this.TextBoxPort.Text.Trim();
}
else
{
this.TextBoxPort.Text = this.port;
this.TextBoxPort.SelectionStart = this.TextBoxPort.Text.Length;
this.TextBoxPort.SelectionLength = 0;
}
}
/// <summary>
/// The method that handles the timer events.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The event args.</param>
private void TimerElapsed(object sender, ElapsedEventArgs e)
{
this.BeginInvoke(
(MethodInvoker)delegate
{
// Server
this.TextBoxPort.Enabled = this.mqttServer == null;
this.ButtonServerStart.Enabled = this.mqttServer == null;
this.ButtonServerStop.Enabled = this.mqttServer != null;
// Publisher
this.ButtonPublisherStart.Enabled = this.managedMqttClientPublisher == null;
this.ButtonPublisherStop.Enabled = this.managedMqttClientPublisher != null;
// Auto Publisher
this.ButtonAutoPublisherStart.Enabled = !this.randomPublisherTimer.Enabled;
this.ButtonAutoPublisherStop.Enabled = this.randomPublisherTimer.Enabled;
// Subscriber
this.ButtonSubscriberStart.Enabled = this.managedMqttClientSubscriber == null;
this.ButtonSubscriberStop.Enabled = this.managedMqttClientSubscriber != null;
});
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
this.label8.Text = $"{this.trackBar1.Value:N0} ms";
this.randomPublisherTimer.Interval = this.trackBar1.Value;
}
private void ButtonAutoPublisherStartClick(object sender, EventArgs e)
{
((Button)sender).Enabled = false;
this.randomPublisherTimer.Start();
((Button)sender).Enabled = true;
}
private void ButtonAutoPublisherStopClick(object sender, EventArgs e)
{
((Button)sender).Enabled = false;
this.randomPublisherTimer.Stop();
((Button)sender).Enabled = true;
}
}
}

View File

@ -0,0 +1,60 @@
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,83 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="JsonServerStorage.cs" company="Haemmer Electronics">
// Copyright (c) 2020 All rights reserved.
// </copyright>
// <summary>
// The JSON server storage.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace MQTTnet.TestApp.WinForm
{
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using MQTTnet.Server;
using Newtonsoft.Json;
/// <inheritdoc cref="IMqttServerStorage"/>
/// <summary>
/// The JSON server storage.
/// </summary>
/// <seealso cref="IMqttServerStorage"/>
public class JsonServerStorage : IMqttServerStorage
{
/// <summary>
/// The file name.
/// </summary>
private readonly string filename = Path.Combine(Directory.GetCurrentDirectory(), "Retained.json");
/// <summary>
/// Clears the file.
/// </summary>
public void Clear()
{
if (File.Exists(this.filename))
{
File.Delete(this.filename);
}
}
/// <inheritdoc cref="IMqttServerStorage"/>
/// <summary>
/// Loads the retained messages.
/// </summary>
/// <returns>A <see cref="IList{T}"/> of <see cref="MqttApplicationMessage"/>.</returns>
/// <seealso cref="IMqttServerStorage"/>
public async Task<IList<MqttApplicationMessage>> LoadRetainedMessagesAsync()
{
await Task.CompletedTask;
if (!File.Exists(this.filename))
{
return new List<MqttApplicationMessage>();
}
try
{
var json = await File.ReadAllTextAsync(this.filename);
return JsonConvert.DeserializeObject<List<MqttApplicationMessage>>(json);
}
catch
{
return new List<MqttApplicationMessage>();
}
}
/// <inheritdoc cref="IMqttServerStorage"/>
/// <summary>
/// Saves the retained messages to a file.
/// </summary>
/// <param name="messages">The messages.</param>
/// <returns>A <see cref="Task"/> representing any asynchronous operation.</returns>
/// <seealso cref="IMqttServerStorage"/>
public async Task SaveRetainedMessagesAsync(IList<MqttApplicationMessage> messages)
{
await Task.CompletedTask;
var json = JsonConvert.SerializeObject(messages);
await File.WriteAllTextAsync(this.filename, json);
}
}
}

View File

@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<RootNamespace>MQTTnet.TestApp.WinForm</RootNamespace>
<AssemblyName>MQTTnet.TestApp.WinForm</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="GitVersionTask" Version="5.5.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="MQTTnet" Version="3.0.13" />
<PackageReference Include="MQTTnet.Extensions.ManagedClient" Version="3.0.13" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,45 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Program.cs" company="Haemmer Electronics">
// Copyright (c) 2020 All rights reserved.
// </copyright>
// <summary>
// The main program.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace MQTTnet.TestApp.WinForm
{
using System;
using System.Threading;
using System.Windows.Forms;
/// <summary>
/// The main program.
/// </summary>
internal static class Program
{
static readonly Mutex mutex = new Mutex(true, "{B9D725A5-48F1-4907-974F-B6C3B9C8C4BB}");
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
private static void Main()
{
if (!mutex.WaitOne(TimeSpan.Zero, true))
return;
try
{
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
finally
{
mutex.ReleaseMutex();
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More