171 Commits

Author SHA1 Message Date
Sem van der Hoeven
1f163325cf [FIX] fixed the panel not showing, it was because of the animation. removed it for now and it works 2020-10-29 17:09:20 +01:00
Sem van der Hoeven
1c7f9cf70a [FIX] fixed doctor buffer overflow 2020-10-28 13:23:20 +01:00
Sem van der Hoeven
5f2e325cb1 [ADD] added chat to all method from docter, it works but there is sometimes an error in the doctor client app that the buffer in OnRead is full, so will try to fix that tomorrow 2020-10-27 21:35:07 +01:00
Sem van der Hoeven
cda8b47ca3 [
ADD] added message sending to all clients, doesn't work yet
2020-10-27 21:24:47 +01:00
Sem van der Hoeven
fab3ed7705 [EDIT] made seperate method for sending message 2020-10-27 20:59:46 +01:00
shinichi
aeb5d59ce9 implemented nextfocuable 2020-10-19 15:32:14 +02:00
shinichi
ab1662f0fe Merge branch 'develop' into NextFocusOnEnter 2020-10-19 15:15:34 +02:00
shinichi
f07c3f9484 moving and fixing 2020-10-19 15:14:01 +02:00
fabjuuuh
2adfcc5bd7 fix 2020-10-19 15:13:27 +02:00
shinichi
3acdc942bc move files and copy code
from stackoverflow :P
2020-10-19 14:53:47 +02:00
shinichi
84cbcb4a6d send data to doctor 2020-10-19 14:35:51 +02:00
Logophilist
8385f09313 Bike animation added 2020-10-19 14:25:09 +02:00
fabjuuuh
3ea42b65ed Merge branch 'dataOnTabs' into develop 2020-10-19 14:11:59 +02:00
Sem van der Hoeven
55d3dd4eee actually fixed overflow exception 2020-10-19 14:09:03 +02:00
fabjuuuh
960327a3e3 Merge remote-tracking branch 'origin/develop' into dataOnTabs 2020-10-19 14:05:55 +02:00
fabjuuuh
083f27411e chart 2020-10-19 14:02:42 +02:00
shinichi
d9c8b7c6df Merge remote-tracking branch 'origin/develop' into develop 2020-10-19 14:02:07 +02:00
shinichi
de9716128c restyle tabheader 2020-10-19 14:02:03 +02:00
Sem van der Hoeven
f934dee2d0 made overflow return 255 instead of 0 2020-10-19 14:01:39 +02:00
Sem van der Hoeven
107d95b81a fixed overflowexception 2020-10-19 13:59:47 +02:00
Sem van der Hoeven
c50f898ec0 Merge branch 'develop' of https://github.com/SemvdH/Proftaak-RH-B4 into develop 2020-10-19 13:32:33 +02:00
Sem van der Hoeven
fb443a33d7 add send message 2020-10-19 13:32:26 +02:00
Sem van der Hoeven
8249632d00 add set resistance 2020-10-19 13:31:29 +02:00
shinichi
134bf235c3 multiple clients works 2020-10-19 13:24:38 +02:00
shinichi
bc738d0fa3 Merge remote-tracking branch 'origin/develop' into develop 2020-10-19 12:52:39 +02:00
shinichi
2ecc90ff2c doctor can now disconnect 2020-10-19 12:52:35 +02:00
Sem van der Hoeven
6a9e2b57ac Merge branch 'develop' of https://github.com/SemvdH/Proftaak-RH-B4 into develop 2020-10-19 12:49:09 +02:00
Sem van der Hoeven
973ff20da4 small typo 2020-10-19 12:49:04 +02:00
shinichi
d98c32a508 Merge remote-tracking branch 'origin/develop' into develop 2020-10-19 12:24:09 +02:00
shinichi
dcf6c3c6d0 client can see if doctor connected 2020-10-19 12:23:52 +02:00
Sem van der Hoeven
689f7030e4 added displaying of last doctor message in vr scene 2020-10-19 12:20:26 +02:00
shinichi
4a238d9207 Auto stash before checking out "HEAD" 2020-10-19 12:07:00 +02:00
Sem van der Hoeven
31be096b94 added small comments 2020-10-19 11:57:42 +02:00
Sem van der Hoeven
7eb7b03d4f Merge branch 'develop' of https://github.com/SemvdH/Proftaak-RH-B4 into develop 2020-10-19 11:55:34 +02:00
Sem van der Hoeven
17a17e3c6d added comments to dataparser 2020-10-19 11:55:20 +02:00
shinichi
163d2321cc username back added to tabs 2020-10-19 11:49:34 +02:00
shinichi
c9e913474c Merge remote-tracking branch 'origin/develop' into develop 2020-10-19 11:26:18 +02:00
shinichi
63b5c6ab73 removed unnecessary code 2020-10-19 11:26:09 +02:00
Sem van der Hoeven
6b355a0b72 added resistance editing from client to vr engine 2020-10-19 11:26:04 +02:00
fabjuuuh
9a46b17067 Merge remote-tracking branch 'origin/develop' into dataOnTabs 2020-10-19 11:20:28 +02:00
shinichi
b748bcf5c8 binded commands 2020-10-19 11:19:57 +02:00
fabjuuuh
62525f3a23 Merge remote-tracking branch 'origin/develop' into dataOnTabs 2020-10-19 11:14:57 +02:00
fabjuuuh
783c40d7d3 rip 2020-10-19 11:14:05 +02:00
shinichi
e9e3381960 icon works 2020-10-16 16:32:17 +02:00
shinichi
75ff7fe336 Merge remote-tracking branch 'origin/develop' into develop 2020-10-16 16:24:13 +02:00
shinichi
cc046d6611 wip 2020-10-16 16:24:07 +02:00
fabjuuuh
5751bbed81 Progress with structure to send data 2020-10-16 16:23:50 +02:00
Sem van der Hoeven
a76433803c Merge branch 'develop' of https://github.com/SemvdH/Proftaak-RH-B4 into develop 2020-10-16 15:39:28 +02:00
Sem van der Hoeven
ae01646de6 added doctor icon 2020-10-16 15:39:17 +02:00
shinichi
e8a4901f09 fix 2020-10-16 15:15:09 +02:00
fabjuuuh
b868515ade Merge branch 'newDoctor' into develop 2020-10-16 15:06:52 +02:00
fabjuuuh
93eeea856f errors 2020-10-16 15:06:38 +02:00
fabjuuuh
0427fa89aa Merge remote-tracking branch 'origin/develop' into newDoctor 2020-10-16 15:04:16 +02:00
fabjuuuh
173dbf2745 update 2020-10-16 15:02:26 +02:00
shinichi
fd9b321292 doctor can see patients who logged on before 2020-10-16 15:02:19 +02:00
fabjuuuh
e3c580c8c2 Merge remote-tracking branch 'origin/develop' into newDoctor 2020-10-16 12:51:42 +02:00
shinichi
6fb9d0efaa Merge branch 'doctor-client-connecting' into develop 2020-10-16 12:49:18 +02:00
shinichi
540a640bc6 removed debuggers 2020-10-16 12:49:06 +02:00
fabjuuuh
aadbbc16a9 Workinng on communication 2020-10-16 12:48:14 +02:00
shinichi
777ed65707 Merge branch 'develop' into doctor-client-connecting 2020-10-16 12:19:04 +02:00
shinichi
44abffd7e7 closing windows works 2020-10-16 12:18:46 +02:00
shinichi
9ef51ddeca connecting to doctor after works 2020-10-16 12:18:03 +02:00
Sem van der Hoeven
2ebfea1a01 Merge branch 'develop' of https://github.com/SemvdH/Proftaak-RH-B4 into develop 2020-10-16 12:16:06 +02:00
Sem van der Hoeven
2f81f32a03 added checking for response from vr engine and automatic reconnect 2020-10-16 12:15:58 +02:00
fabjuuuh
b31fa293fb Commands 2020-10-16 11:42:32 +02:00
Sem van der Hoeven
014191b350 smol fix 2020-10-16 11:27:12 +02:00
shinichi
ac11f6c53b Merge remote-tracking branch 'origin/develop' into develop 2020-10-16 11:25:39 +02:00
shinichi
5bacfdc8ce added so only one doctor can login to server 2020-10-16 11:25:35 +02:00
Sem van der Hoeven
effdd887af Merge branch 'develop' of https://github.com/SemvdH/Proftaak-RH-B4 into develop 2020-10-16 11:24:08 +02:00
Sem van der Hoeven
1c3e48f676 added ralfs message display on vr panel into engineconnection 2020-10-16 11:23:52 +02:00
shinichi
df37c9bd58 made doctor window fancy 2020-10-16 11:18:29 +02:00
shinichi
6ec09ec995 error catching 2020-10-16 10:51:45 +02:00
shinichi
813545c2da Merge branch 'ValueConverters' into develop 2020-10-16 10:41:52 +02:00
shinichi
29037f1774 status is now shown by image 2020-10-16 10:41:38 +02:00
shinichi
5280e70feb wip 2020-10-16 10:13:59 +02:00
shinichi
94be6fd22d Merge remote-tracking branch 'origin/develop' into develop 2020-10-14 16:28:41 +02:00
shinichi
dc2b2f06ae move styles to sharedproject and disposed client 2020-10-14 16:28:37 +02:00
Sem van der Hoeven
30c5d5a2a3 add message to server switch 2020-10-14 15:58:06 +02:00
shinichi
bbb0220c11 Merge branch 'window-restyle' into develop 2020-10-14 15:54:02 +02:00
shinichi
adf8435822 got rid of navigation ui 2020-10-14 15:53:52 +02:00
shinichi
ed19714338 Merge branch 'develop' into window-restyle 2020-10-14 15:30:00 +02:00
shinichi
c13cdf64b6 window works 2020-10-14 15:29:19 +02:00
Sem van der Hoeven
e226ab586e added message method to dataparser 2020-10-14 15:27:35 +02:00
fabjuuuh
6e534c318b Merge branch 'develop' into newDoctor 2020-10-14 15:21:03 +02:00
fabjuuuh
d8a3baeec1 Relaycommands 2020-10-14 15:19:43 +02:00
Sem van der Hoeven
ea4093ff75 added message to dataparser 2020-10-14 15:19:20 +02:00
Sem van der Hoeven
11ebc67c13 moved observableobject to util 2020-10-14 15:01:13 +02:00
Sem van der Hoeven
c5c74a3d7a moved dataparser to util 2020-10-14 14:59:00 +02:00
shinichi
756c646b93 before removing shadows 2020-10-14 14:55:58 +02:00
Sem van der Hoeven
037d7732f7 changed name from hasher to util 2020-10-14 14:55:31 +02:00
Sem van der Hoeven
91a20e0af7 set doctor of communication when doctor logs in 2020-10-14 14:49:53 +02:00
fabjuuuh
af5dca25f4 Remove Tabs when disconnect 2020-10-14 14:35:14 +02:00
shinichi
6bec8f1651 wip 2020-10-14 14:35:02 +02:00
fabjuuuh
3547207cad Merge branch 'develop' into newDoctor 2020-10-14 13:51:19 +02:00
Sem van der Hoeven
b519f33cb5 added new user when username not in file 2020-10-14 13:21:12 +02:00
Sem van der Hoeven
8263d51ca7 removed hashing username 2020-10-14 13:13:59 +02:00
fabjuuuh
ecb3be2916 Merge branch 'newDoctor' into develop 2020-10-14 13:07:20 +02:00
fabjuuuh
25176dbaeb Connections shit 2020-10-14 13:05:56 +02:00
Sem van der Hoeven
c9c0cf35a0 removed maincommand from placehouse sig 2020-10-14 13:05:55 +02:00
Sem van der Hoeven
568896c28d fully added the vr engine stuff to the client app 2020-10-14 12:21:53 +02:00
shinichi
56de6cfa24 wip 2020-10-14 12:05:28 +02:00
Sem van der Hoeven
22021dc821 removed vr engine retry button 2020-10-14 11:28:28 +02:00
Sem van der Hoeven
ea3afdeda1 added automatic reconnect to vr engine 2020-10-14 11:19:39 +02:00
shinichi
3822508b4c added empty resource files 2020-10-14 09:53:49 +02:00
shinichi
2e5dbeee14 added icon 2020-10-14 09:34:47 +02:00
shinichi
ee8333e6af try login failed popup to align
plz someone fix
2020-10-14 08:55:31 +02:00
shinichi
6ed069d0f3 Merge branch 'try-refactor-to-page' into develop 2020-10-14 08:43:11 +02:00
shinichi
fd4fcd0e51 Merge branch 'develop' into try-refactor-to-page 2020-10-14 08:42:53 +02:00
shinichi
073aa8c79a now pages
background image doesn't work so it is lime
2020-10-14 08:42:40 +02:00
fabjuuuh
301f6b447f Progress 2020-10-12 16:50:02 +02:00
fabjuuuh
df5e276eb3 Vastgelopen 2020-10-12 12:50:38 +02:00
Logophilist
87e199d1b5 Showing message on panel 2020-10-12 09:01:33 +02:00
shinichi
9761d189ec background image
i give up
2020-10-09 17:05:11 +02:00
Sem van der Hoeven
8ad861e18a conflicts 2020-10-09 16:59:31 +02:00
Sem van der Hoeven
f6e5152939 connect new gui to vr engine 2020-10-09 16:58:39 +02:00
shinichi
c8a812508f added folder for future use 2020-10-09 16:29:18 +02:00
shinichi
5fcf0d177b wip 2020-10-09 16:22:28 +02:00
shinichi
278a5adb5e change doctor password field to passwordbox 2020-10-09 16:03:49 +02:00
shinichi
528f515e0b Merge remote-tracking branch 'origin/develop' into develop 2020-10-09 15:37:30 +02:00
shinichi
b4088f5d30 edited focusable 2020-10-09 15:37:23 +02:00
Logophilist
34eed08234 Fixed grass texture, houses and time of day 2020-10-09 15:35:54 +02:00
shinichi
e410e9ec88 login failed popup shows 2020-10-09 15:07:27 +02:00
Sem van der Hoeven
25d0bda483 added new reference 2020-10-09 12:52:14 +02:00
Sem van der Hoeven
fafc96ad6a removed client 2020-10-09 12:50:46 +02:00
Sem van der Hoeven
bcdf8587c0 Merge branch 'develop' of https://github.com/SemvdH/Proftaak-RH-B4 into develop 2020-10-09 12:49:22 +02:00
Sem van der Hoeven
5cc008cd03 merged client and clientapp 2020-10-09 12:49:06 +02:00
Sem van der Hoeven
b642e17268 readded clientapp 2020-10-09 12:35:56 +02:00
Sem van der Hoeven
bcc479cf33 temp 2020-10-09 12:27:28 +02:00
shinichi
50a2ae9521 added popup not binded yet 2020-10-09 12:18:34 +02:00
Sem van der Hoeven
c87c190d14 fix dataparser import 2020-10-09 12:06:24 +02:00
Sem van der Hoeven
8e48fe7902 Merge branch 'develop' of https://github.com/SemvdH/Proftaak-RH-B4 into develop 2020-10-09 12:02:34 +02:00
Sem van der Hoeven
39b935ecbc o p t i m i z e 2020-10-09 12:02:23 +02:00
Sem van der Hoeven
68994fd3fe added BLEHANDLER tag to blehandler cw 2020-10-09 12:02:03 +02:00
Sem van der Hoeven
adf6073e42 added client project reference to server 2020-10-09 12:01:48 +02:00
Sem van der Hoeven
94d1c07c99 added client project reference to server 2020-10-09 12:01:34 +02:00
Sem van der Hoeven
2aa736b0a8 formatted speed to show km/h too 2020-10-09 12:01:23 +02:00
Sem van der Hoeven
b5ea137bad removed 20 second sleep 2020-10-09 12:00:51 +02:00
shinichi
dffeb39337 mainView is now binded 2020-10-09 11:40:52 +02:00
shinichi
2cb3fd5ec3 ble and simulation stop when window closes 2020-10-09 11:33:26 +02:00
shinichi
124eee5f12 implemented stop for BLEHandler and BikeSimulator 2020-10-09 11:16:51 +02:00
fabjuuuh
ab9180281e Merge branch 'dokter' into develop 2020-10-09 10:57:16 +02:00
fabjuuuh
a043cdc723 Merge branch 'develop' into dokter 2020-10-09 10:56:48 +02:00
shinichi
143f7e4d88 Merge branch 'move-Client-to-ClientApp' into develop 2020-10-07 21:21:19 +02:00
shinichi
e09f4f1325 changed names 2020-10-07 21:21:12 +02:00
shinichi
a23b268dc9 login working
uuuuggglyyyyyyyyyyyyyy :(
2020-10-07 19:45:20 +02:00
shinichi
7bf5bdb1ce delete 2020-10-07 18:34:04 +02:00
shinichi
46083b3912 copy 2020-10-07 17:56:21 +02:00
shinichi
187beaf6c1 Merge branch 'develop' into Client-GUI 2020-10-07 16:42:17 +02:00
Sem van der Hoeven
d6da6b18c4 added value update to panel and adjusted speed to match, only need to display speed with 1 decimal 2020-10-07 16:40:41 +02:00
shinichi
04b0051e72 Merge branch 'develop' into Client-GUI 2020-10-07 16:32:42 +02:00
shinichi
91bb0d65b7 added status 2020-10-07 16:31:44 +02:00
Sem van der Hoeven
847fa68acb fix for displaying things on vr scene 2020-10-07 16:06:16 +02:00
shinichi
d7dfe32cc3 wip 2020-10-07 15:49:57 +02:00
Sem van der Hoeven
651a44762b input test 2020-10-07 15:15:42 +02:00
shinichi
6159f3f57b added login logic and text 2020-10-07 15:14:39 +02:00
Sem van der Hoeven
046389f3b7 Merge branch 'develop' of https://github.com/SemvdH/Proftaak-RH-B4 into develop 2020-10-07 15:09:16 +02:00
Sem van der Hoeven
57aa9b6deb finished reconnecting on sim not started and then sending the commands 2020-10-07 15:08:45 +02:00
shinichi
6e2b3a80b3 wip 2020-10-07 13:17:40 +02:00
Logophilist
26a784277a Panel now shows more data 2020-10-07 12:58:50 +02:00
Sem van der Hoeven
bff4718d3f added route to client vr scene 2020-10-07 12:51:09 +02:00
Sem van der Hoeven
499134fa9a Merge branch 'develop' of https://github.com/SemvdH/Proftaak-RH-B4 into develop 2020-10-07 12:22:25 +02:00
Sem van der Hoeven
f5eb59694e added comments 2020-10-07 12:22:10 +02:00
shinichi
71ff5dfc81 Merge branch 'fix-getBPMgraphdata' into develop 2020-10-07 11:55:56 +02:00
shinichi
472c514275 removed debugging 2020-10-07 11:55:49 +02:00
shinichi
7c943f4f73 fix 2020-10-07 11:54:47 +02:00
Sem van der Hoeven
f31cdeb467 some writelines 2020-10-07 11:52:14 +02:00
Logophilist
05354b5ebe Merge remote-tracking branch 'origin/develop' into develop 2020-10-07 11:51:07 +02:00
Logophilist
f6c2b3c662 Panel fixed 2020-10-07 11:51:02 +02:00
Sem van der Hoeven
475159bbb2 fix conflicts 2020-10-07 11:24:19 +02:00
Sem van der Hoeven
c2b4b259a3 comment 2020-10-07 11:23:09 +02:00
shinichi
5d14d53df5 wip 2020-10-02 16:29:11 +02:00
87 changed files with 4940 additions and 242 deletions

View File

@@ -31,22 +31,46 @@ namespace Client
client.BeginConnect(adress, port, new AsyncCallback(OnConnect), null);
}
/// <summary>
/// initializes the VR engine and sets the callbacks
/// </summary>
private void initEngine()
{
engineConnection = EngineConnection.INSTANCE;
engineConnection.OnNoTunnelId = retryEngineConnection;
engineConnection.OnSuccessFullConnection = engineConnected;
if (!engineConnection.Connected) engineConnection.Connect();
}
/// <summary>
/// retries to connect to the VR engine if no tunnel id was found
/// </summary>
private void retryEngineConnection()
{
Console.WriteLine("-- Could not connect to the VR engine. Please make sure you are running the simulation!");
Console.WriteLine("-- Press any key to retry connecting to the VR engine.");
Console.ReadKey();
engineConnection.CreateConnection();
Console.WriteLine("-- Press ENTER to retry connecting to the VR engine.");
Console.WriteLine("-- Press 'q' and then ENTER to not connect to the VR engine");
string input = Console.ReadLine();
if (input == string.Empty) engineConnection.CreateConnection();
else
{
Console.WriteLine("Skipping connecting to VR engine...");
engineConnection.Stop();
}
}
private void engineConnected()
{
Console.WriteLine("successfully connected to VR engine");
engineConnection.initScene();
if (engineConnection.Connected && sessionRunning && !engineConnection.FollowingRoute) engineConnection.StartRouteFollow();
}
/// <summary>
/// callback method for when the TCP client is connected
/// </summary>
/// <param name="ar">the result of the async read</param>
private void OnConnect(IAsyncResult ar)
{
this.client.EndConnect(ar);
@@ -60,6 +84,10 @@ namespace Client
this.stream.BeginRead(this.buffer, 0, this.buffer.Length, new AsyncCallback(OnRead), null);
}
/// <summary>
/// callback method for when there is a message read
/// </summary>
/// <param name="ar">the result of the async read</param>
private void OnRead(IAsyncResult ar)
{
int receivedBytes = this.stream.EndRead(ar);
@@ -93,8 +121,9 @@ namespace Client
string responseStatus = DataParser.getResponseStatus(payloadbytes);
if (responseStatus == "OK")
{
Console.WriteLine("Username and password correct!");
this.connected = true;
//initEngine();
initEngine();
}
else
{
@@ -103,14 +132,18 @@ namespace Client
}
break;
case DataParser.START_SESSION:
Console.WriteLine("Session started!");
this.sessionRunning = true;
if (engineConnection.Connected && !engineConnection.FollowingRoute) engineConnection.StartRouteFollow();
sendMessage(DataParser.getStartSessionJson());
break;
case DataParser.STOP_SESSION:
Console.WriteLine("Stop session identifier");
this.sessionRunning = false;
sendMessage(DataParser.getStopSessionJson());
break;
case DataParser.SET_RESISTANCE:
Console.WriteLine("Set resistance identifier");
if (this.handler == null)
{
Console.WriteLine("handler is null");
@@ -140,18 +173,31 @@ namespace Client
}
/// <summary>
/// starts sending a message to the server
/// </summary>
/// <param name="message">the message to send</param>
private void sendMessage(byte[] message)
{
stream.BeginWrite(message, 0, message.Length, new AsyncCallback(OnWrite), null);
}
/// <summary>
/// callback method for when a message is fully written to the server
/// </summary>
/// <param name="ar">the async result representing the asynchronous call</param>
private void OnWrite(IAsyncResult ar)
{
this.stream.EndWrite(ar);
}
#region interface
//maybe move this to other place
/// <summary>
/// bpm method for receiving the BPM value from the bluetooth bike or the simulation
/// </summary>
/// <param name="bytes">the message</param>
public void BPM(byte[] bytes)
{
if (!sessionRunning)
@@ -163,11 +209,24 @@ namespace Client
throw new ArgumentNullException("no bytes");
}
byte[] message = DataParser.GetRawDataMessage(bytes);
if (engineConnection.Connected && engineConnection.FollowingRoute)
{
engineConnection.BikeBPM = bytes[1];
}
this.stream.BeginWrite(message, 0, message.Length, new AsyncCallback(OnWrite), null);
}
/// <summary>
/// method for receiving the bike message from the bluetooth bike or the simulation
/// </summary>
/// <param name="bytes">the message</param>
public void Bike(byte[] bytes)
{
bool canSendToEngine = engineConnection.Connected && engineConnection.FollowingRoute;
if (!sessionRunning)
{
return;
@@ -177,15 +236,34 @@ namespace Client
throw new ArgumentNullException("no bytes");
}
byte[] message = DataParser.GetRawDataMessage(bytes);
switch (bytes[0])
{
case 0x10:
if (canSendToEngine) engineConnection.BikeSpeed = (bytes[4] | (bytes[5] << 8)) * 0.01f;
break;
case 0x19:
if (canSendToEngine) engineConnection.BikePower = (bytes[5]) | (bytes[6] & 0b00001111) << 8;
break;
}
this.stream.BeginWrite(message, 0, message.Length, new AsyncCallback(OnWrite), null);
}
#endregion
/// <summary>
/// wether or not the client stream is connected
/// </summary>
/// <returns>true if it's connected, false if not</returns>
public bool IsConnected()
{
return this.connected;
}
/// <summary>
/// tries to log in to the server by asking for a username and password
/// </summary>
private void tryLogin()
{
//TODO File in lezen
@@ -203,17 +281,10 @@ namespace Client
this.stream.BeginWrite(message, 0, message.Length, new AsyncCallback(OnWrite), null);
}
public void tryLoginDoctor(string username, string password)
{
string hashUser = Hashing.Hasher.HashString(username);
string hashPassword = Hashing.Hasher.HashString(password);
byte[] message = DataParser.getJsonMessage(DataParser.GetLoginJson(hashUser, hashPassword));
stream.BeginWrite(message, 0, message.Length, new AsyncCallback(OnWrite), null);
}
/// <summary>
/// sets the handler for the client, so either the bike simulator or the bluetooth bike handler
/// </summary>
/// <param name="handler"></param>
public void setHandler(IHandler handler)
{
this.handler = handler;

View File

@@ -8,12 +8,15 @@ namespace Client
{
public delegate void HandleSerial(string message);
public delegate void HandleNoTunnelId();
public delegate void OnSuccessfullConnection();
public sealed class EngineConnection
{
private static EngineConnection instance = null;
private static readonly object padlock = new object();
private static System.Timers.Timer updateTimer;
public HandleNoTunnelId OnNoTunnelId;
public OnSuccessfullConnection OnSuccessFullConnection;
private static PC[] PCs = {
@@ -29,9 +32,18 @@ namespace Client
private static ServerResponseReader serverResponseReader;
private static string sessionId = string.Empty;
private static string tunnelId = string.Empty;
private static string cameraId = string.Empty;
private static string routeId = string.Empty;
private static string panelId = string.Empty;
private static string bikeId = string.Empty;
private static string headId = string.Empty;
public float BikeSpeed { get; set; }
public float BikePower { get; set; }
public float BikeBPM { get; set; }
public float BikeResistance { get; set; }
public bool FollowingRoute = false;
private static NetworkStream stream;
@@ -42,9 +54,24 @@ namespace Client
EngineConnection()
{
BikeSpeed = 0;
BikePower = 0;
BikeBPM = 0;
BikeResistance = 50;
updateTimer = new System.Timers.Timer(1000);
updateTimer.Elapsed += UpdateTimer_Elapsed;
updateTimer.AutoReset = true;
updateTimer.Enabled = false;
}
private void UpdateTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
UpdateInfoPanel();
}
/// <summary>
/// Singleton constructor
/// </summary>
public static EngineConnection INSTANCE
{
get
@@ -60,6 +87,11 @@ namespace Client
}
}
/// <summary>
/// connects to the vr engine and initalizes the serverResponseReader
/// </summary>
public void Connect()
{
TcpClient client = new TcpClient("145.48.6.10", 6666);
@@ -68,6 +100,18 @@ namespace Client
CreateConnection();
}
/// <summary>
/// initializes and starts the reading of the responses from the vr server
/// </summary>
/// <param name="stream">the networkstream</param>
private void initReader()
{
serverResponseReader = new ServerResponseReader(stream);
serverResponseReader.callback = HandleResponse;
serverResponseReader.StartRead();
}
#region VR Message traffic
/// <summary>
/// connects to the server and creates the tunnel
/// </summary>
@@ -84,26 +128,8 @@ namespace Client
WriteTextMessage(tunnelCreate);
// wait until we have a tunnel id
while (tunnelId == string.Empty) { }
if (tunnelId != null)
{
Write("got tunnel id! " + tunnelId);
}
mainCommand = new Command(tunnelId);
}
/// <summary>
/// initializes and starts the reading of the responses from the vr server
/// </summary>
/// <param name="stream">the networkstream</param>
private void initReader()
{
serverResponseReader = new ServerResponseReader(stream);
serverResponseReader.callback = HandleResponse;
serverResponseReader.StartRead();
Connected = true;
}
/// <summary>
/// callback method that handles responses from the server
@@ -121,12 +147,19 @@ namespace Client
else if (id == "tunnel/create")
{
tunnelId = JSONParser.GetTunnelID(message);
Console.WriteLine("set tunnel id to " + tunnelId);
if (tunnelId == null)
{
Write("could not find a valid tunnel id!");
OnNoTunnelId?.Invoke();
Connected = false;
FollowingRoute = false;
return;
} else
{
Write("got tunnel id! " + tunnelId);
Connected = true;
OnSuccessFullConnection?.Invoke();
}
}
@@ -139,6 +172,101 @@ namespace Client
}
}
public void initScene()
{
Write("initializing scene...");
mainCommand = new Command(tunnelId);
// reset the scene
WriteTextMessage(mainCommand.ResetScene());
//Get sceneinfo and set the id's
SendMessageAndOnResponse(mainCommand.GetSceneInfoCommand("sceneinfo"), "sceneinfo",
(message) =>
{
//Console.WriteLine("\r\n\r\n\r\nscene info" + message);
cameraId = JSONParser.GetIdSceneInfoChild(message, "Camera");
string headId = JSONParser.GetIdSceneInfoChild(message, "Head");
string handLeftId = JSONParser.GetIdSceneInfoChild(message, "LeftHand");
string handRightId = JSONParser.GetIdSceneInfoChild(message, "RightHand");
});
// add the route and set the route id
SendMessageAndOnResponse(mainCommand.RouteCommand("routeID"), "routeID", (message) => routeId = JSONParser.GetResponseUuid(message));
}
internal void StartRouteFollow()
{
Write("Starting route follow...");
FollowingRoute = true;
SendMessageAndOnResponse(mainCommand.AddBikeModel("bikeID"), "bikeID",
(message) =>
{
bikeId = JSONParser.GetResponseUuid(message);
SendMessageAndOnResponse(mainCommand.addPanel("panelAdd", bikeId), "panelAdd",
(message) =>
{
panelId = JSONParser.getPanelID(message);
WriteTextMessage(mainCommand.ColorPanel(panelId));
UpdateInfoPanel();
updateTimer.Enabled = true;
while (cameraId == string.Empty) { }
SetFollowSpeed(5.0f);
});
});
}
public void UpdateInfoPanel()
{
ShowPanel(BikeSpeed, (int)BikeBPM, (int)BikePower, (int)BikeResistance);
WriteTextMessage(mainCommand.RouteSpeed(BikeSpeed, bikeId));
WriteTextMessage(mainCommand.RouteSpeed(BikeSpeed, cameraId));
}
public void ShowPanel(double bikeSpeed, int bpm, int power, int resistance)
{
WriteTextMessage(mainCommand.ClearPanel(panelId));
SendMessageAndOnResponse(mainCommand.showBikespeed(panelId, "bikeSpeed", bikeSpeed), "bikeSpeed",
(message) =>
{
// TODO check if is drawn
});
SendMessageAndOnResponse(mainCommand.showHeartrate(panelId, "bpm", bpm), "bpm",
(message) =>
{
// TODO check if is drawn
});
SendMessageAndOnResponse(mainCommand.showPower(panelId, "power", power), "power",
(message) =>
{
// TODO check if is drawn
});
SendMessageAndOnResponse(mainCommand.showResistance(panelId, "resistance", resistance), "resistance",
(message) =>
{
// TODO check if is drawn
});
// Check if every text is drawn!
WriteTextMessage(mainCommand.SwapPanel(panelId));
}
private void SetFollowSpeed(float speed)
{
WriteTextMessage(mainCommand.RouteFollow(routeId, bikeId, speed, new float[] { 0, -(float)Math.PI / 2f, 0 }, new float[] { 0, 0, 0 }));
WriteTextMessage(mainCommand.RouteFollow(routeId, cameraId, speed));
}
#endregion
#region message send/receive
/// <summary>
/// method that sends the speciefied message with the specified serial, and executes the given action upon receivind a reply from the server with this serial.
/// </summary>
@@ -147,8 +275,14 @@ namespace Client
/// <param name="serial">the serial to check for</param>
/// <param name="action">the code to be executed upon reveiving a reply from the server with the specified serial</param>
public void SendMessageAndOnResponse(string message, string serial, HandleSerial action)
{
if (serialResponses.ContainsKey(serial))
{
serialResponses[serial] = action;
} else
{
serialResponses.Add(serial, action);
}
WriteTextMessage(message);
}
@@ -169,6 +303,14 @@ namespace Client
//Write("sent message " + message);
}
#endregion
public void Stop()
{
serverResponseReader.Stop();
}
public void Write(string msg)
{
Console.WriteLine( "[ENGINECONNECT] " + msg);

View File

@@ -12,10 +12,8 @@ namespace Client
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Console.WriteLine("// Connecting... //");
//connect fiets?
Thread.Sleep(20000);
Client client = new Client();
@@ -36,9 +34,9 @@ namespace Client
//client.setHandler(bikeSimulator);
while (true)
{
}
//while (true)
//{
//}
}
}
}

30
ClientApp/App.xaml Normal file
View File

@@ -0,0 +1,30 @@
<Application x:Class="ClientApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ClientApp"
xmlns:viewModels="clr-namespace:ClientApp.ViewModels"
xmlns:views="clr-namespace:ClientApp.Views"
StartupUri="Views/MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type viewModels:MainViewModel}">
<views:MainView />
<!-- This is a Page -->
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:LoginViewModel}">
<views:LoginView />
<!-- This is a Page -->
</DataTemplate>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="WPFStuff/Styles/Fonts.xaml"/>
<ResourceDictionary Source="WPFStuff/Styles/Colors.xaml"/>
<ResourceDictionary Source="WPFStuff/Styles/Buttons.xaml"/>
<ResourceDictionary Source="WPFStuff/Styles/Texts.xaml"/>
<ResourceDictionary Source="WPFStuff/Styles/Windows.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

17
ClientApp/App.xaml.cs Normal file
View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace ClientApp
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

10
ClientApp/AssemblyInfo.cs Normal file
View File

@@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

View File

@@ -0,0 +1,66 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWPF>true</UseWPF>
<ApplicationIcon>Images\Logo\icon1.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<None Remove="Images\CoolBackground.jpg" />
<None Remove="Images\Icons\CheckMark.png" />
<None Remove="Images\Icons\CrossMark.png" />
<None Remove="Images\Logo\icon1.ico" />
<None Remove="Images\re15.jpg" />
<None Remove="Images\stone.png" />
</ItemGroup>
<ItemGroup>
<Content Include="Images\CoolBackground.jpg">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Images\Icons\CheckMark.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Images\Icons\CrossMark.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Images\Logo\icon1.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Images\re15.jpg">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Images\stone.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="AsyncAwaitBestPractices.MVVM" Version="4.3.0" />
<PackageReference Include="MvvmLightLibsStd10" Version="5.4.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="PropertyChanged.Fody" Version="3.2.9" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ProftaakRH\ProftaakRH.csproj" />
<ProjectReference Include="..\RH-Engine\RH-Engine.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<Import Project="..\Hashing\Hashing.projitems" Label="Shared" />
</Project>

View File

@@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<PropertyChanged />
</Weavers>

74
ClientApp/FodyWeavers.xsd Normal file
View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="PropertyChanged" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="InjectOnPropertyNameChanged" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if the On_PropertyName_Changed feature is enabled.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="TriggerDependentProperties" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if the Dependent properties feature is enabled.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="EnableIsChangedProperty" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if the IsChanged property feature is enabled.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="EventInvokerNames" type="xs:string">
<xs:annotation>
<xs:documentation>Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="CheckForEquality" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="CheckForEqualityUsingBaseEquals" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if equality checks should use the Equals method resolved from the base class.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="UseStaticEqualsFromBase" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if equality checks should use the static Equals method resolved from the base class.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="SuppressWarnings" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to turn off build warnings from this weaver.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="SuppressOnPropertyNameChangedWarning" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to turn off build warnings about mismatched On_PropertyName_Changed methods.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
ClientApp/Images/re15.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
ClientApp/Images/stone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,212 @@
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace ClientApp.MagicCode
{
/// <summary>
/// Fixes the issue with Windows of Style <see cref="WindowStyle.None"/> covering the taskbar
/// </summary>
public class WindowResizer
{
#region Private Members
/// <summary>
/// The window to handle the resizing for
/// </summary>
private Window mWindow;
#endregion
#region Dll Imports
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetCursorPos(out POINT lpPoint);
[DllImport("user32.dll")]
static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr MonitorFromPoint(POINT pt, MonitorOptions dwFlags);
#endregion
#region Constructor
/// <summary>
/// Default constructor
/// </summary>
/// <param name="window">The window to monitor and correctly maximize</param>
/// <param name="adjustSize">The callback for the host to adjust the maximum available size if needed</param>
public WindowResizer(Window window)
{
mWindow = window;
// Listen out for source initialized to setup
mWindow.SourceInitialized += Window_SourceInitialized;
}
#endregion
#region Initialize
/// <summary>
/// Initialize and hook into the windows message pump
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Window_SourceInitialized(object sender, System.EventArgs e)
{
// Get the handle of this window
var handle = (new WindowInteropHelper(mWindow)).Handle;
var handleSource = HwndSource.FromHwnd(handle);
// If not found, end
if (handleSource == null)
return;
// Hook into it's Windows messages
handleSource.AddHook(WindowProc);
}
#endregion
#region Windows Proc
/// <summary>
/// Listens out for all windows messages for this window
/// </summary>
/// <param name="hwnd"></param>
/// <param name="msg"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <param name="handled"></param>
/// <returns></returns>
private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
// Handle the GetMinMaxInfo of the Window
case 0x0024:/* WM_GETMINMAXINFO */
WmGetMinMaxInfo(hwnd, lParam);
handled = true;
break;
}
return (IntPtr)0;
}
#endregion
/// <summary>
/// Get the min/max window size for this window
/// Correctly accounting for the taskbar size and position
/// </summary>
/// <param name="hwnd"></param>
/// <param name="lParam"></param>
private void WmGetMinMaxInfo(System.IntPtr hwnd, System.IntPtr lParam)
{
POINT lMousePosition;
GetCursorPos(out lMousePosition);
IntPtr lPrimaryScreen = MonitorFromPoint(new POINT(0, 0), MonitorOptions.MONITOR_DEFAULTTOPRIMARY);
MONITORINFO lPrimaryScreenInfo = new MONITORINFO();
if (GetMonitorInfo(lPrimaryScreen, lPrimaryScreenInfo) == false)
{
return;
}
IntPtr lCurrentScreen = MonitorFromPoint(lMousePosition, MonitorOptions.MONITOR_DEFAULTTONEAREST);
MINMAXINFO lMmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));
if (lPrimaryScreen.Equals(lCurrentScreen) == true)
{
lMmi.ptMaxPosition.X = lPrimaryScreenInfo.rcWork.Left;
lMmi.ptMaxPosition.Y = lPrimaryScreenInfo.rcWork.Top;
lMmi.ptMaxSize.X = lPrimaryScreenInfo.rcWork.Right - lPrimaryScreenInfo.rcWork.Left;
lMmi.ptMaxSize.Y = lPrimaryScreenInfo.rcWork.Bottom - lPrimaryScreenInfo.rcWork.Top;
}
else
{
lMmi.ptMaxPosition.X = lPrimaryScreenInfo.rcMonitor.Left;
lMmi.ptMaxPosition.Y = lPrimaryScreenInfo.rcMonitor.Top;
lMmi.ptMaxSize.X = lPrimaryScreenInfo.rcMonitor.Right - lPrimaryScreenInfo.rcMonitor.Left;
lMmi.ptMaxSize.Y = lPrimaryScreenInfo.rcMonitor.Bottom - lPrimaryScreenInfo.rcMonitor.Top;
}
// Now we have the max size, allow the host to tweak as needed
Marshal.StructureToPtr(lMmi, lParam, true);
}
}
#region Dll Helper Structures
enum MonitorOptions : uint
{
MONITOR_DEFAULTTONULL = 0x00000000,
MONITOR_DEFAULTTOPRIMARY = 0x00000001,
MONITOR_DEFAULTTONEAREST = 0x00000002
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class MONITORINFO
{
public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));
public Rectangle rcMonitor = new Rectangle();
public Rectangle rcWork = new Rectangle();
public int dwFlags = 0;
}
[StructLayout(LayoutKind.Sequential)]
public struct Rectangle
{
public int Left, Top, Right, Bottom;
public Rectangle(int left, int top, int right, int bottom)
{
this.Left = left;
this.Top = top;
this.Right = right;
this.Bottom = bottom;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct MINMAXINFO
{
public POINT ptReserved;
public POINT ptMaxSize;
public POINT ptMaxPosition;
public POINT ptMinTrackSize;
public POINT ptMaxTrackSize;
};
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
/// <summary>
/// x coordinate of point.
/// </summary>
public int X;
/// <summary>
/// y coordinate of point.
/// </summary>
public int Y;
/// <summary>
/// Construct a point of coordinates (x,y).
/// </summary>
public POINT(int x, int y)
{
this.X = x;
this.Y = y;
}
}
#endregion
}

17
ClientApp/Models/Info.cs Normal file
View File

@@ -0,0 +1,17 @@
using ClientApp.Utils;
using System;
using System.Collections.Generic;
using System.Text;
using Util;
namespace ClientApp.Models
{
class Info : ObservableObject
{
public bool ConnectedToServer { get; set; }
public bool ConnectedToVREngine { get; set; }
public bool DoctorConnected { get; set; }
public bool CanConnectToVR { get; set; }
}
}

327
ClientApp/Utils/Client.cs Normal file
View File

@@ -0,0 +1,327 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using ClientApp.ViewModels;
using ProftaakRH;
using Util;
namespace ClientApp.Utils
{
public delegate void EngineCallback();
public class Client : IDataReceiver, IDisposable
{
public EngineCallback engineConnectFailed;
public EngineCallback engineConnectSuccess;
private TcpClient client;
private NetworkStream stream;
private byte[] buffer = new byte[1024];
private bool connected;
private byte[] totalBuffer = new byte[1024];
private int totalBufferReceived = 0;
public EngineConnection engineConnection;
private bool sessionRunning = false;
private IHandler handler = null;
private LoginViewModel LoginViewModel;
public Client() : this("localhost", 5555)
{
}
public Client(string adress, int port)
{
this.client = new TcpClient();
this.connected = false;
client.BeginConnect(adress, port, new AsyncCallback(OnConnect), null);
}
/// <summary>
/// initializes the VR engine and sets the callbacks
/// </summary>
private void initEngine()
{
Debug.WriteLine("[CLIENT] init engine");
engineConnection = EngineConnection.INSTANCE;
engineConnection.OnNoTunnelId = RetryEngineConnection;
engineConnection.OnSuccessFullConnection = engineConnected;
engineConnection.OnEngineDisconnect = RetryEngineConnection;
if (!engineConnection.Connected) engineConnection.Connect();
}
/// <summary>
/// retries to connect to the VR engine if no tunnel id was found
/// </summary>
public void RetryEngineConnection()
{
engineConnectFailed?.Invoke();
}
private void engineConnected()
{
Console.WriteLine("successfully connected to VR engine");
engineConnectSuccess?.Invoke();
engineConnection.initScene();
if (engineConnection.Connected && sessionRunning && !engineConnection.FollowingRoute) engineConnection.StartRouteFollow();
}
/// <summary>
/// callback method for when the TCP client is connected
/// </summary>
/// <param name="ar">the result of the async read</param>
private void OnConnect(IAsyncResult ar)
{
this.client.EndConnect(ar);
Console.WriteLine("TCP client Verbonden!");
this.stream = this.client.GetStream();
this.stream.BeginRead(this.buffer, 0, this.buffer.Length, new AsyncCallback(OnRead), null);
}
/// <summary>
/// callback method for when there is a message read
/// </summary>
/// <param name="ar">the result of the async read</param>
private void OnRead(IAsyncResult ar)
{
Debug.WriteLine("got message in client app");
if (ar == null || (!ar.IsCompleted) || (!this.stream.CanRead))
return;
int receivedBytes = this.stream.EndRead(ar);
if (totalBufferReceived + receivedBytes > 1024)
{
throw new OutOfMemoryException("buffer too small");
}
Array.Copy(buffer, 0, totalBuffer, totalBufferReceived, receivedBytes);
totalBufferReceived += receivedBytes;
int expectedMessageLength = BitConverter.ToInt32(totalBuffer, 0);
while (totalBufferReceived >= expectedMessageLength)
{
//volledig packet binnen
byte[] messageBytes = new byte[expectedMessageLength];
Array.Copy(totalBuffer, 0, messageBytes, 0, expectedMessageLength);
byte[] payloadbytes = new byte[BitConverter.ToInt32(messageBytes, 0) - 5];
Array.Copy(messageBytes, 5, payloadbytes, 0, payloadbytes.Length);
string identifier;
bool isJson = DataParser.getJsonIdentifier(messageBytes, out identifier);
if (isJson)
{
switch (identifier)
{
case DataParser.LOGIN_RESPONSE:
string responseStatus = DataParser.getResponseStatus(payloadbytes);
if (responseStatus == "OK")
{
Debug.WriteLine("Username and password correct!");
this.LoginViewModel.setLoginStatus(true);
this.connected = true;
initEngine();
}
else
{
this.LoginViewModel.setLoginStatus(false);
Debug.WriteLine($"login failed \"{responseStatus}\"");
}
break;
case DataParser.START_SESSION:
Debug.WriteLine("Session started!");
this.sessionRunning = true;
if (engineConnection.Connected && !engineConnection.FollowingRoute) engineConnection.StartRouteFollow();
Debug.WriteLine("start");
break;
case DataParser.STOP_SESSION:
Console.WriteLine("Stop session identifier");
this.sessionRunning = false;
Debug.WriteLine("stop");
break;
case DataParser.SET_RESISTANCE:
Debug.WriteLine("Set resistance identifier");
if (this.handler == null)
{
// send that the operation was not successful if the handler is null
Debug.WriteLine("handler is null");
sendMessage(DataParser.getSetResistanceResponseJson(false));
}
else
{
// set the resistance in the vr scene and send that it was successful
float resistance = DataParser.getResistanceFromJson(payloadbytes);
Debug.WriteLine("resistance set was " + resistance);
this.handler.setResistance(resistance);
engineConnection.BikeResistance = resistance;
sendMessage(DataParser.getSetResistanceResponseJson(true));
}
break;
case DataParser.MESSAGE:
Debug.WriteLine("client has received message from doctor");
engineConnection.DoctorMessage = DataParser.getChatMessageFromJson(payloadbytes);
break;
case DataParser.NEW_CONNECTION:
this.LoginViewModel.DoctorConnected(DataParser.getUsernameFromJson(payloadbytes));
break;
case DataParser.DISCONNECT:
this.LoginViewModel.DoctorDisconnected(DataParser.getUsernameFromJson(payloadbytes));
break;
default:
Console.WriteLine($"Received json with identifier {identifier}:\n{Encoding.ASCII.GetString(payloadbytes)}");
break;
}
}
else if (DataParser.isRawDataBikeServer(messageBytes))
{
Console.WriteLine($"Received data: {BitConverter.ToString(payloadbytes)}");
}
totalBufferReceived -= expectedMessageLength;
expectedMessageLength = BitConverter.ToInt32(totalBuffer, 0);
}
if (ar == null || (!ar.IsCompleted) || (!this.stream.CanRead) || !this.client.Connected)
return;
ar.AsyncWaitHandle.WaitOne();
this.stream.BeginRead(this.buffer, 0, this.buffer.Length, new AsyncCallback(OnRead), null);
}
/// <summary>
/// starts sending a message to the server
/// </summary>
/// <param name="message">the message to send</param>
public void sendMessage(byte[] message)
{
stream.BeginWrite(message, 0, message.Length, new AsyncCallback(OnWrite), null);
}
/// <summary>
/// callback method for when a message is fully written to the server
/// </summary>
/// <param name="ar">the async result representing the asynchronous call</param>
private void OnWrite(IAsyncResult ar)
{
this.stream.EndWrite(ar);
}
#region interface
//maybe move this to other place
/// <summary>
/// bpm method for receiving the BPM value from the bluetooth bike or the simulation
/// </summary>
/// <param name="bytes">the message</param>
public void BPM(byte[] bytes)
{
if (!sessionRunning)
{
return;
}
if (bytes == null)
{
throw new ArgumentNullException("no bytes");
}
byte[] message = DataParser.GetRawBPMDataMessageServer(bytes);
if (engineConnection.Connected && engineConnection.FollowingRoute)
{
engineConnection.BikeBPM = bytes[1];
}
this.stream.BeginWrite(message, 0, message.Length, new AsyncCallback(OnWrite), null);
}
/// <summary>
/// method for receiving the bike message from the bluetooth bike or the simulation
/// </summary>
/// <param name="bytes">the message</param>
public void Bike(byte[] bytes)
{
if (!sessionRunning)
{
return;
}
if (bytes == null)
{
throw new ArgumentNullException("no bytes");
}
byte[] message = DataParser.GetRawBikeDataMessageServer(bytes);
bool canSendToEngine = engineConnection.Connected && engineConnection.FollowingRoute;
switch (bytes[0])
{
case 0x10:
if (canSendToEngine) engineConnection.BikeSpeed = (bytes[4] | (bytes[5] << 8)) * 0.01f;
break;
case 0x19:
if (canSendToEngine) engineConnection.BikePower = (bytes[5]) | (bytes[6] & 0b00001111) << 8;
break;
}
this.stream.BeginWrite(message, 0, message.Length, new AsyncCallback(OnWrite), null);
}
#endregion
/// <summary>
/// wether or not the client stream is connected
/// </summary>
/// <returns>true if it's connected, false if not</returns>
public bool IsConnected()
{
return this.connected;
}
/// <summary>
/// tries to log in to the server by asking for a username and password
/// </summary>
public void tryLogin(string username, string password)
{
string hashPassword = Util.Hasher.HashString(password);
byte[] message = DataParser.getJsonMessage(DataParser.GetLoginJson(username, hashPassword));
this.stream.BeginWrite(message, 0, message.Length, new AsyncCallback(OnWrite), null);
}
/// <summary>
/// sets the handler for the client, so either the bike simulator or the bluetooth bike handler
/// </summary>
/// <param name="handler"></param>
public void SetHandler(IHandler handler)
{
this.handler = handler;
}
internal void SetLoginViewModel(LoginViewModel loginViewModel)
{
this.LoginViewModel = loginViewModel;
}
public void Dispose()
{
Debug.WriteLine("client dispose called");
sendMessage(DataParser.getDisconnectJson(LoginViewModel.Username));
this.stream.Dispose();
this.client.Dispose();
this.handler.stop();
this.engineConnection.Stop();
}
}
}

View File

@@ -0,0 +1,438 @@
using System;
using System.Collections.Generic;
using System.Text;
using RH_Engine;
using System.Net.Sockets;
using Newtonsoft.Json.Linq;
using System.Diagnostics;
using LibNoise.Primitive;
namespace ClientApp.Utils
{
public delegate void HandleSerial(string message);
public delegate void EngineDelegate();
public sealed class EngineConnection
{
private static EngineConnection instance = null;
private static readonly object padlock = new object();
private static System.Timers.Timer updateTimer;
private static System.Timers.Timer noVRResponseTimer;
public EngineDelegate OnNoTunnelId;
public EngineDelegate OnSuccessFullConnection;
public EngineDelegate OnEngineDisconnect;
private static PC[] PCs = {
//new PC("DESKTOP-M2CIH87", "Fabian"),
//new PC("T470S", "Shinichi"),
//new PC("DESKTOP-DHS478C", "semme"),
new PC("HP-ZBOOK-SEM", "Sem")
//new PC("DESKTOP-TV73FKO", "Wouter"),
//new PC("DESKTOP-SINMKT1", "Ralf van Aert"),
//new PC("NA", "Bart")
};
private static ServerResponseReader serverResponseReader;
private static string sessionId = string.Empty;
private static string tunnelId = string.Empty;
private static string cameraId = string.Empty;
private static string routeId = string.Empty;
private static string panelId = string.Empty;
private static string bikeId = string.Empty;
private static string headId = string.Empty;
private static string groundPlaneId = string.Empty;
private static string terrainId = string.Empty;
public string DoctorMessage { get; set; }
public float BikeSpeed { get; set; }
public float BikePower { get; set; }
public float BikeBPM { get; set; }
public float BikeResistance { get; set; }
public bool FollowingRoute = false;
private static NetworkStream stream;
private static Dictionary<string, HandleSerial> serialResponses = new Dictionary<string, HandleSerial>();
private Command mainCommand;
public bool Connected = false;
EngineConnection()
{
BikeSpeed = 0;
BikePower = 0;
BikeBPM = 0;
BikeResistance = 50;
DoctorMessage = "No message received yet";
updateTimer = new System.Timers.Timer(1000);
updateTimer.Elapsed += UpdateTimer_Elapsed;
updateTimer.AutoReset = true;
updateTimer.Enabled = false;
noVRResponseTimer = new System.Timers.Timer(30000);
noVRResponseTimer.Elapsed += noVRResponseTimeout;
noVRResponseTimer.AutoReset = false;
noVRResponseTimer.Enabled = false;
}
private void UpdateTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
UpdateInfoPanel();
}
private void noVRResponseTimeout(object sender, System.Timers.ElapsedEventArgs e)
{
Write("VR RESPONSE TIMEOUT");
noVRResponseTimer.Stop();
sessionId = string.Empty;
tunnelId = string.Empty;
cameraId = string.Empty;
routeId = string.Empty;
panelId = string.Empty;
bikeId = string.Empty;
groundPlaneId = string.Empty;
terrainId = string.Empty;
OnEngineDisconnect?.Invoke();
}
/// <summary>
/// Singleton constructor
/// </summary>
public static EngineConnection INSTANCE
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new EngineConnection();
}
}
return instance;
}
}
/// <summary>
/// connects to the vr engine and initalizes the serverResponseReader
/// </summary>
public void Connect()
{
TcpClient client = new TcpClient("145.48.6.10", 6666);
stream = client.GetStream();
initReader();
CreateConnection();
}
/// <summary>
/// initializes and starts the reading of the responses from the vr server
/// </summary>
/// <param name="stream">the networkstream</param>
private void initReader()
{
serverResponseReader = new ServerResponseReader(stream);
serverResponseReader.callback = HandleResponse;
serverResponseReader.StartRead();
}
#region VR Message traffic
/// <summary>
/// connects to the server and creates the tunnel
/// </summary>
/// <param name="stream">the network stream to use</param>
public void CreateConnection()
{
WriteTextMessage("{\r\n\"id\" : \"session/list\",\r\n\"serial\" : \"list\"\r\n}");
// wait until we have got a sessionId
while (sessionId == string.Empty) { }
string tunnelCreate = "{\"id\" : \"tunnel/create\", \"data\" : {\"session\" : \"" + sessionId + "\"}}";
WriteTextMessage(tunnelCreate);
}
/// <summary>
/// callback method that handles responses from the server
/// </summary>
/// <param name="message">the response message from the server</param>
public void HandleResponse(string message)
{
string id = JSONParser.GetID(message);
// because the first messages don't have a serial, we need to check on the id
if (id == "session/list")
{
sessionId = JSONParser.GetSessionID(message, PCs);
}
else if (id == "tunnel/create")
{
tunnelId = JSONParser.GetTunnelID(message);
Console.WriteLine("set tunnel id to " + tunnelId);
if (tunnelId == null)
{
//Write("could not find a valid tunnel id!");
OnNoTunnelId?.Invoke();
Connected = false;
FollowingRoute = false;
return;
}
else
{
Write("got tunnel id! " + tunnelId);
Connected = true;
noVRResponseTimer.Enabled = true;
OnSuccessFullConnection?.Invoke();
}
}
if (message.Contains("serial"))
{
//Console.WriteLine("GOT MESSAGE WITH SERIAL: " + message + "\n\n\n");
string serial = JSONParser.GetSerial(message);
//Console.WriteLine("Got serial " + serial);
if (serialResponses.ContainsKey(serial)) serialResponses[serial].Invoke(message);
}
noVRResponseTimer.Stop();
noVRResponseTimer.Start();
}
public void initScene()
{
Write("initializing scene...");
mainCommand = new Command(tunnelId);
// reset the scene
WriteTextMessage(mainCommand.ResetScene());
//Get sceneinfo and set the id's
SendMessageAndOnResponse(mainCommand.GetSceneInfoCommand("sceneinfo"), "sceneinfo",
(message) =>
{
//Console.WriteLine("\r\n\r\n\r\nscene info" + message);
cameraId = JSONParser.GetIdSceneInfoChild(message, "Camera");
string headId = JSONParser.GetIdSceneInfoChild(message, "Head");
string handLeftId = JSONParser.GetIdSceneInfoChild(message, "LeftHand");
string handRightId = JSONParser.GetIdSceneInfoChild(message, "RightHand");
groundPlaneId = JSONParser.GetIdSceneInfoChild(message, "GroundPlane");
});
// add the route and set the route id
CreateTerrain();
SendMessageAndOnResponse(mainCommand.RouteCommand("routeID"), "routeID", (message) => routeId = JSONParser.GetResponseUuid(message));
}
internal void StartRouteFollow()
{
Write("Starting route follow...");
FollowingRoute = true;
SendMessageAndOnResponse(mainCommand.AddBikeModel("bikeID"), "bikeID",
(message) =>
{
bikeId = JSONParser.GetResponseUuid(message);
SendMessageAndOnResponse(mainCommand.addPanel("panelAdd", bikeId), "panelAdd",
(message) =>
{
Write("got panel id");
panelId = JSONParser.getPanelID(message);
WriteTextMessage(mainCommand.ColorPanel(panelId));
UpdateInfoPanel();
updateTimer.Enabled = true;
while (cameraId == string.Empty) { }
SetFollowSpeed(5.0f);
WriteTextMessage(mainCommand.RoadCommand(routeId, "road"));
WriteTextMessage(mainCommand.ShowRoute("showRouteFalse", false));
});
});
setEnvironment();
}
private void setEnvironment()
{
Write("Setting environment");
WriteTextMessage(mainCommand.DeleteNode(groundPlaneId, "none"));
PlaceHouses();
WriteTextMessage(mainCommand.SkyboxCommand(DateTime.Now.Hour));
}
private void PlaceHouses()
{
PlaceHouse(2, new float[] { 10f, 1f, 30f }, 1);
PlaceHouse(1, new float[] { 42f, 1f, 22f }, new float[] { 0f, 90f, 0f }, 2);
PlaceHouse(11, new float[] { -20f, 1f, 0f }, new float[] { 0f, -35f, 0f }, 3);
PlaceHouse(7, new float[] { -15f, 1f, 50f }, new float[] { 0f, -50f, 0f }, 4);
PlaceHouse(24, new float[] { 40f, 1f, 40f }, new float[] { 0f, 75f, 0f }, 5);
PlaceHouse(22, new float[] { 34f, 1f, -20f }, 6);
PlaceHouse(14, new float[] { 0f, 1f, -20f }, new float[] { 0f, 210f, 0f }, 7);
}
private void PlaceHouse(int numberHousemodel, float[] position, int serialNumber)
{
PlaceHouse(numberHousemodel, position, new float[] { 0f, 0f, 0f }, serialNumber);
}
private void PlaceHouse(int numberHousemodel, float[] position, float[] rotation, int serialNumber)
{
string folderHouses = @"data\NetworkEngine\models\houses\set1\";
SendMessageAndOnResponse(mainCommand.AddModel("House1", "housePlacement" + serialNumber, folderHouses + "house" + numberHousemodel + ".obj", position, 4, rotation), "housePlacement" + serialNumber, (message) => Console.WriteLine(message));
}
public void UpdateInfoPanel()
{
Write("updating info panel");
ShowPanel(BikeSpeed, (int)BikeBPM, (int)BikePower, (int)BikeResistance);
WriteTextMessage(mainCommand.RouteSpeed(BikeSpeed, bikeId));
WriteTextMessage(mainCommand.RouteSpeed(BikeSpeed, cameraId));
}
public void ShowPanel(double bikeSpeed, int bpm, int power, int resistance)
{
WriteTextMessage(mainCommand.ClearPanel(panelId));
SendMessageAndOnResponse(mainCommand.showBikespeed(panelId, "bikeSpeed", bikeSpeed), "bikeSpeed",
(message) =>
{
// TODO check if is drawn
});
SendMessageAndOnResponse(mainCommand.showHeartrate(panelId, "bpm", bpm), "bpm",
(message) =>
{
// TODO check if is drawn
});
SendMessageAndOnResponse(mainCommand.showPower(panelId, "power", power), "power",
(message) =>
{
// TODO check if is drawn
});
SendMessageAndOnResponse(mainCommand.showResistance(panelId, "resistance", resistance), "resistance",
(message) =>
{
// TODO check if is drawn
});
SendMessageAndOnResponse(mainCommand.showMessage(panelId, "message", DoctorMessage), "message",
(message) =>
{
// TODO check if is drawn
});
// Check if every text is drawn!
WriteTextMessage(mainCommand.SwapPanel(panelId));
}
private void SetFollowSpeed(float speed)
{
Write("starting route follow");
WriteTextMessage(mainCommand.RouteFollow(routeId, bikeId, speed, new float[] { 0, -(float)Math.PI / 2f, 0 }, new float[] { 0, 0, 0 }));
WriteTextMessage(mainCommand.RouteFollow(routeId, cameraId, speed));
}
public void CreateTerrain()
{
float x = 0f;
float[] height = new float[256 * 256];
ImprovedPerlin improvedPerlin = new ImprovedPerlin(0, LibNoise.NoiseQuality.Best);
for (int i = 0; i < 256 * 256; i++)
{
height[i] = improvedPerlin.GetValue(x / 10, x / 10, x * 100) / 3.5f + 1;
//if (height[i] > 1.1f)
//{
// height[i] = height[i] * 0.8f;
//}
//else if (height[i] < 0.9f)
//{
// height[i] = height[i] * 1.2f;
//}
x += 0.001f;
}
SendMessageAndOnResponse(mainCommand.TerrainAdd(new int[] { 256, 256 }, height, "terrain"), "terrain",
(message) =>
{
SendMessageAndOnResponse(mainCommand.renderTerrain("renderTerrain"), "renderTerrain",
(message) =>
{
terrainId = JSONParser.GetTerrainID(message);
string addLayerMsg = mainCommand.AddLayer(terrainId, "addLayer");
SendMessageAndOnResponse(addLayerMsg, "addLayer", (message) => Console.WriteLine(""));
});
});
}
#endregion
#region message send/receive
/// <summary>
/// method that sends the speciefied message with the specified serial, and executes the given action upon receivind a reply from the server with this serial.
/// </summary>
/// <param name="stream">the networkstream to use</param>
/// <param name="message">the message to send</param>
/// <param name="serial">the serial to check for</param>
/// <param name="action">the code to be executed upon reveiving a reply from the server with the specified serial</param>
public void SendMessageAndOnResponse(string message, string serial, HandleSerial action)
{
if (serialResponses.ContainsKey(serial))
{
serialResponses[serial] = action;
}
else
{
serialResponses.Add(serial, action);
}
WriteTextMessage(message);
}
/// <summary>
/// writes a message to the server
/// </summary>
/// <param name="stream">the network stream to use</param>
/// <param name="message">the message to send</param>
public void WriteTextMessage(string message)
{
byte[] msg = Encoding.ASCII.GetBytes(message);
byte[] res = new byte[msg.Length + 4];
Array.Copy(BitConverter.GetBytes(msg.Length), 0, res, 0, 4);
Array.Copy(msg, 0, res, 4, msg.Length);
stream.Write(res);
//Write("sent message " + message);
}
#endregion
public void Stop()
{
serverResponseReader.Stop();
}
public void Write(string msg)
{
Debug.WriteLine("[ENGINECONNECT] " + msg);
}
}
}

View File

@@ -0,0 +1,42 @@
using System;
using Hardware;
using Hardware.Simulators;
using RH_Engine;
using System.Security.Cryptography;
using System.Text;
namespace ClientApp.Utils
{
class Program
{
//static void Main(string[] args)
//{
// Console.WriteLine("// Connecting... //");
// //connect fiets?
// Client client = new Client();
// while (!client.IsConnected())
// {
// }
// //BLEHandler bLEHandler = new BLEHandler(client);
// //bLEHandler.Connect();
// //client.setHandler(bLEHandler);
// BikeSimulator bikeSimulator = new BikeSimulator(client);
// bikeSimulator.StartSimulation();
// client.setHandler(bikeSimulator);
// while (true)
// {
// }
//}
}
}

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Data;
using System.Windows.Media.Imaging;
namespace ClientApp.ValueConverters
{
//[ValueConversion(typeof(bool), typeof(BitmapImage))]
class BoolToMarkConverter : IValueConverter
{
public BitmapImage TrueImage { get; set; } = new BitmapImage(new Uri("pack://application:,,,/Images/Icons/CheckMark.png"));
public BitmapImage FalseImage { get; set; } = new BitmapImage(new Uri("pack://application:,,,/Images/Icons/CrossMark.png"));
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (!(value is bool))
{
return null;
}
bool b = (bool)value;
if (b)
{
return this.TrueImage;
}
else
{
return this.FalseImage;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Input;
using ClientApp.Utils;
using GalaSoft.MvvmLight.Command;
using Util;
namespace ClientApp.ViewModels
{
class LoginViewModel : ObservableObject
{
public string Username { get; set; }
public ICommand LoginCommand { get; set; }
public bool LoginStatus { get; set; }
public bool InvertedLoginStatus { get; set; }
private MainWindowViewModel MainWindowViewModel;
public LoginViewModel(MainWindowViewModel mainWindowViewModel)
{
this.MainWindowViewModel = mainWindowViewModel;
LoginCommand = new RelayCommand<object>((parameter) =>
{
//TODO send username and password to server
this.MainWindowViewModel.client.tryLogin(Username, ((PasswordBox)parameter).Password);
});
}
internal void setLoginStatus(bool status)
{
this.MainWindowViewModel.InfoModel.ConnectedToServer = status;
this.InvertedLoginStatus = !status;
if (status)
{
this.MainWindowViewModel.SelectedViewModel = new MainViewModel(MainWindowViewModel);
}
}
internal void DoctorConnected(string name)
{
this.MainWindowViewModel.InfoModel.DoctorConnected = true;
}
internal void DoctorDisconnected(string name)
{
this.MainWindowViewModel.InfoModel.DoctorConnected = false;
}
}
}

View File

@@ -0,0 +1,46 @@
using ClientApp.Models;
using ClientApp.Utils;
using GalaSoft.MvvmLight.Command;
using System.Diagnostics;
using System.Windows.Input;
using Util;
namespace ClientApp.ViewModels
{
class MainViewModel : ObservableObject
{
public ICommand RetryServerCommand { get; set; }
public MainWindowViewModel MainWindowViewModel { get; set; }
private Client client;
public MainViewModel(MainWindowViewModel mainWindowViewModel)
{
this.MainWindowViewModel = mainWindowViewModel;
client = this.MainWindowViewModel.client;
client.engineConnectFailed = retryEngineConnection;
client.engineConnectSuccess = succesEngineConnection;
this.RetryServerCommand = new RelayCommand(() =>
{
//try connect server
this.MainWindowViewModel.InfoModel.ConnectedToServer = true;
});
}
private void retryEngineConnection()
{
this.MainWindowViewModel.InfoModel.ConnectedToVREngine = false;
this.MainWindowViewModel.InfoModel.CanConnectToVR = true;
client.engineConnection.CreateConnection();
}
private void succesEngineConnection()
{
this.MainWindowViewModel.InfoModel.ConnectedToVREngine = true;
this.MainWindowViewModel.InfoModel.CanConnectToVR = false;
}
}
}

View File

@@ -0,0 +1,137 @@
using ClientApp.MagicCode;
using ClientApp.Models;
using ClientApp.Utils;
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Util;
using System.Windows;
using System.Windows.Input;
namespace ClientApp.ViewModels
{
class MainWindowViewModel : ObservableObject
{
#region private members
private Window mWindow;
private int mOuterMarginSize = 10;
private int mWindowRadius = 10;
#endregion
#region commands
public ICommand MinimizeCommand { get; set; }
public ICommand MaximizeCommand { get; set; }
public ICommand CloseCommand { get; set; }
public ICommand MenuCommand { get; set; }
#endregion
#region public properties
public Info InfoModel { get; set; }
public ObservableObject SelectedViewModel { get; set; }
public Client client { get; }
/// <summary>
/// size of the resize border around the window
/// </summary>
public double MinimumWidth { get; set; } = 250;
public double MinimumHeight { get; set; } = 250;
public int ResizeBorder { get; set; } = 6;
public Thickness ResizeBorderThickness { get { return new Thickness(ResizeBorder + OuterMarginSize); } }
public Thickness InnerContentPadding { get { return new Thickness(ResizeBorder); } }
public Thickness OuterMarginThickness { get { return new Thickness(OuterMarginSize); } }
public CornerRadius WindowCornerRadius { get { return new CornerRadius(WindowRadius); } }
public int OuterMarginSize
{
get
{
return mWindow.WindowState == WindowState.Maximized ? 0 : mOuterMarginSize;
}
set
{
mOuterMarginSize = value;
}
}
public int WindowRadius
{
get
{
return mWindow.WindowState == WindowState.Maximized ? 0 : mWindowRadius;
}
set
{
mWindowRadius = value;
}
}
public int TitleHeight { get; set; } = 42;
public GridLength TitleHeightGridLength { get { return new GridLength(TitleHeight + ResizeBorder); } }
#endregion
public MainWindowViewModel(Window window, Client client)
{
this.mWindow = window;
this.mWindow.StateChanged += (sender, e) =>
{
OnPropertyChanged(nameof(ResizeBorderThickness));
OnPropertyChanged(nameof(OuterMarginThickness));
OnPropertyChanged(nameof(WindowCornerRadius));
OnPropertyChanged(nameof(OuterMarginSize));
OnPropertyChanged(nameof(WindowRadius));
};
this.InfoModel = new Info();
this.client = client;
LoginViewModel loginViewModel = new LoginViewModel(this);
SelectedViewModel = loginViewModel;
this.client.SetLoginViewModel(loginViewModel);
this.MinimizeCommand = new RelayCommand(() => this.mWindow.WindowState = WindowState.Minimized);
this.MaximizeCommand = new RelayCommand(() => this.mWindow.WindowState ^= WindowState.Maximized);
this.CloseCommand = new RelayCommand(() => this.mWindow.Close());
this.MenuCommand = new RelayCommand(() => SystemCommands.ShowSystemMenu(this.mWindow, GetMousePosition()));
var resizer = new WindowResizer(this.mWindow);
this.mWindow.Closed += (sender, e) => this.client.Dispose();
}
#region helper
private Point GetMousePosition()
{
Debug.WriteLine("getmousePosition called");
var p = Mouse.GetPosition(this.mWindow);
return new Point(p.X + this.mWindow.Left, p.Y + this.mWindow.Top);
}
#endregion
}
}

View File

@@ -0,0 +1,27 @@
<Page x:Class="ClientApp.Views.LoginView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ClientApp.Views"
xmlns:viewModels="clr-namespace:ClientApp.ViewModels"
xmlns:Util="clr-namespace:Util.MagicCode"
mc:Ignorable="d"
ShowsNavigationUI="False"
d:DesignHeight="450" d:DesignWidth="800">
<DockPanel>
<DockPanel.Background>
<ImageBrush TileMode="Tile" ViewportUnits="Absolute" Viewport="0 0 64 64" ImageSource="\images\stone.png"/>
</DockPanel.Background>
<StackPanel VerticalAlignment="Center" Width="auto">
<Label Content="Username" HorizontalContentAlignment="Center" />
<TextBox x:Name="Username" Text="{Binding Username}" TextWrapping="Wrap" Width="120" Util:FocusAdvancement.AdvancesByEnterKey="True" />
<Label Content="Password" HorizontalContentAlignment="Center"/>
<PasswordBox x:Name="Password" Width="120" Util:FocusAdvancement.AdvancesByEnterKey="True"/>
<Button x:Name="Login" Content="Login" Command="{Binding LoginCommand}" CommandParameter="{Binding ElementName=Password}" Margin="0,20,0,0" Width="120"/>
<Popup IsOpen="{Binding InvertedLoginStatus}" PopupAnimation = "Slide" HorizontalAlignment="Center">
<Label Content="Login failed" Foreground="Red" HorizontalAlignment="Center" HorizontalContentAlignment="Center" Background="Transparent"/>
</Popup>
</StackPanel>
</DockPanel>
</Page>

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace ClientApp.Views
{
/// <summary>
/// Interaction logic for LoginView.xaml
/// </summary>
public partial class LoginView : Page
{
public LoginView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,40 @@
<Page x:Class="ClientApp.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ClientApp.Views"
xmlns:converter="clr-namespace:ClientApp.ValueConverters"
ShowsNavigationUI="False"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Page.Resources>
<converter:BoolToMarkConverter x:Key="BoolToMarkConverter"/>
</Page.Resources>
<DockPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Grid.Row="0" Orientation="Horizontal" Margin="20" HorizontalAlignment="Center">
<Label Content="Connected to server:" VerticalAlignment="Center"/>
<Image Source="{Binding Converter={StaticResource BoolToMarkConverter},Path=MainWindowViewModel.InfoModel.ConnectedToServer}" Stretch="Uniform" Width="20" Height="20"/>
</StackPanel>
<StackPanel Grid.Column="1" Grid.Row="0" Orientation="Horizontal" Margin="20" HorizontalAlignment="Center">
<Label Content="Connected to VR-Engine:" VerticalAlignment="Center"/>
<Image Source="{Binding Converter={StaticResource BoolToMarkConverter},Path=MainWindowViewModel.InfoModel.ConnectedToVREngine}" Stretch="Uniform" Width="20" Height="20"/>
</StackPanel>
<StackPanel Grid.Column="2" Grid.Row="0" Orientation="Horizontal" Margin="20" HorizontalAlignment="Center">
<Label Content="Doctor connected:" VerticalAlignment="Center"/>
<Image Source="{Binding Converter={StaticResource BoolToMarkConverter},Path=MainWindowViewModel.InfoModel.DoctorConnected}" Stretch="Uniform" Width="20" Height="20"/>
</StackPanel>
</Grid>
</DockPanel>
</Page>

View File

@@ -0,0 +1,27 @@
using ClientApp.ViewModels;
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace ClientApp.Views
{
/// <summary>
/// Interaction logic for MainView.xaml
/// </summary>
public partial class MainView : Page
{
public MainView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,128 @@
<Window x:Class="ClientApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ClientApp"
xmlns:views="clr-namespace:ClientApp.Views"
xmlns:images="clrnamespace.Images"
mc:Ignorable="d"
WindowStyle="None"
AllowsTransparency="True"
x:Name="applicatonWindow"
WindowStartupLocation="CenterScreen"
MinHeight="{Binding MinimumHeight}"
MinWidth="{Binding MinimumWidth}"
Title="Whaazzzzuuuuuuuup">
<!--Icon="/Images/Logo/icon1_small.ico"-->
<!--Icon="pack://application:,,,/Images/Log/icon1_small.ico"-->
<Window.Resources>
<Style TargetType="{x:Type local:MainWindow}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Border Padding="{Binding OuterMarginThickness, FallbackValue=10}" >
<Grid>
<!-- opacity mask -->
<Border x:Name="Container"
Background="{StaticResource BackgroundLightBrush}"
CornerRadius="{Binding WindowCornerRadius, FallbackValue=10}"/>
<Border CornerRadius="{Binding WindowCornerRadius, FallbackValue=10}"
Background="{StaticResource BackgroundVeryLightBrush}">
<Border.Effect>
<DropShadowEffect ShadowDepth="0" Opacity="0.2"/>
</Border.Effect>
</Border>
<Grid>
<Grid.OpacityMask>
<VisualBrush Visual="{Binding ElementName=Container}"/>
</Grid.OpacityMask>
<Grid.RowDefinitions>
<RowDefinition Height="{Binding TitleHeightGridLength, FallbackValue=42}"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Title bar -->
<Grid Grid.Column="0" Grid.Row="0" Panel.ZIndex="1" Background="{StaticResource BackgroundLightBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- icon -->
<Button Grid.Column="0" Style="{StaticResource SystemIconButton}" Command="{Binding MenuCommand}">
<Image Source="/Images/Logo/icon1.ico"/>
<!--<TextBlock Text="icon"/>-->
</Button>
<!-- Title -->
<Viewbox Grid.Column="1" Margin="0">
<TextBlock Style="{StaticResource HeaderText}" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Title, FallbackValue=failed}"/>
</Viewbox>
<!-- Window buttons -->
<StackPanel Grid.Column="2" Orientation="Horizontal">
<Button Style="{StaticResource WindowControlButton}" Content="_" Command="{Binding MinimizeCommand}"/>
<Button Style="{StaticResource WindowControlButton}" Content="[]" Command="{Binding MaximizeCommand}"/>
<Button Style="{StaticResource WindowCloseButton}" Content="X" Command="{Binding CloseCommand}"/>
</StackPanel>
</Grid>
<!-- Page content -->
<Border Grid.Row="1" Grid.Column="0">
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
<!-- shadow? -->
<Border Grid.Row="1" Height="6" BorderThickness="0 0.2 0 0" VerticalAlignment="Top">
<!--<Border.BorderBrush>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="{StaticResource BackgroundSemiLight}" Offset="0.0"/>
<GradientStop Color="{StaticResource BackgroundVeryLight}" Offset="1.0"/>
</LinearGradientBrush>
</Border.BorderBrush>-->
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="Transparent" Offset="1.0"/>
<GradientStop Color="#7000" Offset="0.0"/>
</LinearGradientBrush>
</Border.Background>
</Border>
</Grid>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<WindowChrome.WindowChrome>
<WindowChrome
ResizeBorderThickness="{Binding ResizeBorderThickness}"
CaptionHeight="{Binding TitleHeight}"
CornerRadius="0"
GlassFrameThickness="0"/>
</WindowChrome.WindowChrome>
<Grid>
<Frame Content="{Binding SelectedViewModel}" Focusable="False" NavigationUIVisibility="Hidden"/>
<Label Content="gemaakt door: mensen" DockPanel.Dock="Bottom" HorizontalAlignment="Right" VerticalAlignment="Bottom" FontStyle="Italic" Foreground="Gray"/>
</Grid>
</Window>

View File

@@ -0,0 +1,57 @@
using ClientApp.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using ClientApp.Utils;
using Hardware.Simulators;
using System.Threading;
using ProftaakRH;
namespace ClientApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private IHandler handler;
public MainWindow()
{
Client client = new Client();
InitializeComponent();
DataContext = new MainWindowViewModel(this, client);
//BLEHandler bLEHandler = new BLEHandler(client);
//bLEHandler.Connect();
//client.setHandler(bLEHandler);
BikeSimulator bikeSimulator = new BikeSimulator(client);
Thread newThread = new Thread(new ThreadStart(bikeSimulator.StartSimulation));
newThread.Start();
client.SetHandler(bikeSimulator);
handler = bikeSimulator;
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
handler.stop();
}
}
}

32
DoctorApp/App.xaml Normal file
View File

@@ -0,0 +1,32 @@
<Application x:Class="DoctorApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DoctorApp"
xmlns:viewModels="clr-namespace:DoctorApp.ViewModels"
xmlns:views="clr-namespace:DoctorApp.Views"
StartupUri="Views/MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type viewModels:MainViewModel}">
<views:MainView />
<!-- This is a Page -->
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:LoginViewModel}">
<views:LoginView />
<!-- This is a Page -->
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:ClientInfoViewModel}">
<views:ClientInfoView/>
</DataTemplate>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="WPFStuff/Styles/Fonts.xaml"/>
<ResourceDictionary Source="WPFStuff/Styles/Colors.xaml"/>
<ResourceDictionary Source="WPFStuff/Styles/Buttons.xaml"/>
<ResourceDictionary Source="WPFStuff/Styles/Texts.xaml"/>
<ResourceDictionary Source="WPFStuff/Styles/Windows.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

17
DoctorApp/App.xaml.cs Normal file
View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace DoctorApp
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

10
DoctorApp/AssemblyInfo.cs Normal file
View File

@@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

View File

@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWPF>true</UseWPF>
<ApplicationIcon>doctor.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<None Remove="img\doctor.ico" />
<None Remove="img\patient.png" />
</ItemGroup>
<ItemGroup>
<Content Include="img\doctor.ico">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="img\patient.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="AsyncAwaitBestPractices.MVVM" Version="4.3.0" />
<PackageReference Include="LiveCharts.Wpf" Version="0.9.7" />
<PackageReference Include="MvvmLightLibsStd10" Version="5.4.1.1" />
<PackageReference Include="PropertyChanged.Fody" Version="3.2.9" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ProftaakRH\ProftaakRH.csproj" />
</ItemGroup>
<Import Project="..\Hashing\Hashing.projitems" Label="Shared" />
</Project>

View File

@@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<PropertyChanged />
</Weavers>

74
DoctorApp/FodyWeavers.xsd Normal file
View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="PropertyChanged" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="InjectOnPropertyNameChanged" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if the On_PropertyName_Changed feature is enabled.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="TriggerDependentProperties" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if the Dependent properties feature is enabled.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="EnableIsChangedProperty" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if the IsChanged property feature is enabled.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="EventInvokerNames" type="xs:string">
<xs:annotation>
<xs:documentation>Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="CheckForEquality" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="CheckForEqualityUsingBaseEquals" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if equality checks should use the Equals method resolved from the base class.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="UseStaticEqualsFromBase" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if equality checks should use the static Equals method resolved from the base class.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="SuppressWarnings" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to turn off build warnings from this weaver.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="SuppressOnPropertyNameChangedWarning" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to turn off build warnings about mismatched On_PropertyName_Changed methods.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

127
DoctorApp/Models/Chart.cs Normal file
View File

@@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using LiveCharts;
using LiveCharts.Configurations;
using Util;
namespace DoctorApp.Models
{
class Chart : ObservableObject,INotifyPropertyChanged
{
private double _axisMax;
private double _axisMin;
private double _trend;
public event PropertyChangedEventHandler PropertyChanged;
public ChartValues<MeasureModel> ChartValues { get; set; }
public Func<double, string> DateTimeFormatter { get; set; }
public PatientInfo PatientInfo { get; set; }
public double AxisStep { get; set; }
public double AxisUnit { get; set; }
public double AxisMax
{
get { return _axisMax; }
set
{
_axisMax = value;
OnPropertyChanged("AxisMax");
}
}
public double AxisMin
{
get { return _axisMin; }
set
{
_axisMin = value;
OnPropertyChanged("AxisMin");
}
}
public bool IsReading { get; set; }
public Chart(PatientInfo patientInfo)
{
var mapper = Mappers.Xy<MeasureModel>()
.X(model => model.DateTime.Ticks)
.Y(model => model.Value);
Charting.For<MeasureModel>(mapper);
ChartValues = new ChartValues<MeasureModel>();
DateTimeFormatter = value => new DateTime((long)value).ToString("mm:ss");
AxisStep = TimeSpan.FromSeconds(1).Ticks;
AxisUnit = TimeSpan.TicksPerSecond;
SetAxisLimits(DateTime.Now);
IsReading = true;
ChartValues.Add(new MeasureModel
{
DateTime = DateTime.Now,
Value = 8
});
}
private void SetAxisLimits(DateTime now)
{
AxisMax = now.Ticks + TimeSpan.FromSeconds(1).Ticks; // lets force the axis to be 1 second ahead
AxisMin = now.Ticks - TimeSpan.FromSeconds(8).Ticks; // and 8 seconds behind
}
protected virtual void OnPropertyChanged(string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void NewValue(double value)
{
var now = DateTime.Now;
_trend = value;
ChartValues.Add(new MeasureModel
{
DateTime = now,
Value = _trend
});
SetAxisLimits(now);
if (ChartValues.Count > 150) ChartValues.RemoveAt(0);
}
public void Clear()
{
Debug.WriteLine("clear");
ChartValues.Clear();
}
}
public class MeasureModel
{
public DateTime DateTime { get; set; }
public double Value { get; set; }
}
}

17
DoctorApp/Models/Info.cs Normal file
View File

@@ -0,0 +1,17 @@
using DoctorApp.Utils;
using System;
using System.Collections.Generic;
using System.Text;
using Util;
namespace DoctorApp.Models
{
class Info : ObservableObject
{
public bool ConnectedToServer { get; set; }
public bool ConnectedToVREngine { get; set; }
public bool DoctorConnected { get; set; }
public bool CanConnectToVR { get; set; }
}
}

View File

@@ -0,0 +1,25 @@
using System.Collections.ObjectModel;
using Util;
namespace DoctorApp.Models
{
class PatientInfo : ObservableObject
{
public ObservableCollection<string> ChatLog { get; set; }
public string Username { get; set; }
public string Status { get; set; }
public double Speed { get; set; }
public int BPM { get; set; }
public float Resistance { get; set; }
public int Acc_Power { get; set; }
public int Curr_Power { get; set; }
public int Distance { get; set; }
}
}

206
DoctorApp/Utils/Client.cs Normal file
View File

@@ -0,0 +1,206 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using DoctorApp.ViewModels;
using ProftaakRH;
using Util;
namespace DoctorApp.Utils
{
public delegate void EngineCallback();
public class Client
{
private TcpClient client;
private NetworkStream stream;
private byte[] buffer = new byte[1024];
private bool connected;
private byte[] totalBuffer = new byte[1024];
private int totalBufferReceived = 0;
private LoginViewModel LoginViewModel;
private MainViewModel MainViewModel;
private ClientInfoViewModel ClientInfoViewModel;
public Client() : this("localhost", 5555)
{
}
public Client(string adress, int port)
{
this.client = new TcpClient();
this.connected = false;
client.BeginConnect(adress, port, new AsyncCallback(OnConnect), null);
}
/// <summary>
/// callback method for when the TCP client is connected
/// </summary>
/// <param name="ar">the result of the async read</param>
private void OnConnect(IAsyncResult ar)
{
this.client.EndConnect(ar);
Console.WriteLine("TCP client Verbonden!");
this.stream = this.client.GetStream();
this.stream.BeginRead(this.buffer, 0, this.buffer.Length, new AsyncCallback(OnRead), null);
}
/// <summary>
/// callback method for when there is a message read
/// </summary>
/// <param name="ar">the result of the async read</param>
private void OnRead(IAsyncResult ar)
{
if (ar == null || (!ar.IsCompleted) || (!this.stream.CanRead))
return;
int receivedBytes = this.stream.EndRead(ar);
if (totalBufferReceived + receivedBytes > 1024)
{
throw new OutOfMemoryException("buffer too small");
}
Array.Copy(buffer, 0, totalBuffer, totalBufferReceived, receivedBytes);
totalBufferReceived += receivedBytes;
int expectedMessageLength = BitConverter.ToInt32(totalBuffer, 0);
while (totalBufferReceived >= expectedMessageLength)
{
//volledig packet binnen
byte[] messageBytes = new byte[expectedMessageLength];
Array.Copy(totalBuffer, 0, messageBytes, 0, expectedMessageLength);
byte[] payloadbytes = new byte[BitConverter.ToInt32(messageBytes, 0) - 5];
Array.Copy(messageBytes, 5, payloadbytes, 0, payloadbytes.Length);
string identifier;
bool isJson = DataParser.getJsonIdentifier(messageBytes, out identifier);
if (isJson)
{
switch (identifier)
{
case DataParser.LOGIN_RESPONSE:
string responseStatus = DataParser.getResponseStatus(payloadbytes);
if (responseStatus == "OK")
{
Debug.WriteLine("Doctor Username and password correct!");
this.LoginViewModel.setLoginStatus(true);
this.connected = true;
}
else
{
this.LoginViewModel.setLoginStatus(false);
Debug.WriteLine($"login failed \"{responseStatus}\"");
}
break;
case DataParser.START_SESSION:
Console.WriteLine("Session started!");
break;
case DataParser.STOP_SESSION:
Console.WriteLine("Stop session identifier");
break;
case DataParser.SET_RESISTANCE:
Console.WriteLine("Set resistance identifier");
break;
case DataParser.NEW_CONNECTION:
this.MainViewModel.NewConnectedUser(DataParser.getUsernameFromJson(payloadbytes));
break;
case DataParser.DISCONNECT:
this.MainViewModel.DisconnectedUser(DataParser.getUsernameFromJson(payloadbytes));
break;
default:
Console.WriteLine($"Received json with identifier {identifier}:\n{Encoding.ASCII.GetString(payloadbytes)}");
break;
}
}
else if (DataParser.isRawDataBikeDoctor(messageBytes))
{
MainViewModel.TransferDataToClientBike(payloadbytes);
}
else if (DataParser.isRawDataBPMDoctor(messageBytes))
{
MainViewModel.TransferDataToClientBPM(payloadbytes);
}
Array.Copy(totalBuffer, expectedMessageLength, totalBuffer, 0, (totalBufferReceived - expectedMessageLength)); //maybe unsafe idk
totalBufferReceived -= expectedMessageLength;
expectedMessageLength = BitConverter.ToInt32(totalBuffer, 0);
}
if (ar == null || (!ar.IsCompleted) || (!this.stream.CanRead) || !this.client.Connected)
return;
this.stream.BeginRead(this.buffer, 0, this.buffer.Length, new AsyncCallback(OnRead), null);
}
/// <summary>
/// starts sending a message to the server
/// </summary>
/// <param name="message">the message to send</param>
public void sendMessage(byte[] message)
{
stream.BeginWrite(message, 0, message.Length, new AsyncCallback(OnWrite), null);
}
/// <summary>
/// callback method for when a message is fully written to the server
/// </summary>
/// <param name="ar">the async result representing the asynchronous call</param>
private void OnWrite(IAsyncResult ar)
{
this.stream.EndWrite(ar);
}
/// <summary>
/// wether or not the client stream is connected
/// </summary>
/// <returns>true if it's connected, false if not</returns>
public bool IsConnected()
{
return this.connected;
}
/// <summary>
/// tries to log in to the server by asking for a username and password
/// </summary>
public void tryLogin(string username, string password)
{
string hashPassword = Util.Hasher.HashString(password);
byte[] message = DataParser.getJsonMessage(DataParser.LoginAsDoctor(username, hashPassword));
this.stream.BeginWrite(message, 0, message.Length, new AsyncCallback(OnWrite), null);
}
internal void SetLoginViewModel(LoginViewModel loginViewModel)
{
this.LoginViewModel = loginViewModel;
}
internal void SetMainViewModel(MainViewModel mainViewModel)
{
this.MainViewModel = mainViewModel;
}
internal void SetClientInfoViewModel(ClientInfoViewModel clientInfoViewModel)
{
this.ClientInfoViewModel = clientInfoViewModel;
}
public void Dispose()
{
Debug.WriteLine("client dispose called");
sendMessage(DataParser.getDisconnectJson(LoginViewModel.Username));
this.stream.Dispose();
this.client.Dispose();
}
}
}

View File

@@ -0,0 +1,150 @@
using DoctorApp.Models;
using DoctorApp.Utils;
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using System.Windows.Controls;
using System.Windows.Input;
using Util;
namespace DoctorApp.ViewModels
{
class ClientInfoViewModel : ObservableObject
{
public PatientInfo PatientInfo { get; set; }
private string _mySelectedItem;
public string MySelectedItem
{
get { return _mySelectedItem; }
set
{
Chart.Clear();
_mySelectedItem = value;
}
}
public ICommand StartSession { get; set; }
public ICommand StopSession { get; set; }
public ICommand Chat { get; set; }
public ICommand ChatToAll { get; set; }
public ICommand ClientInfo { get; set; }
public ICommand SetResistance { get; set; }
public MainWindowViewModel MainWindowViewModel { get; set; }
private Client client;
private MainViewModel parent;
public Chart Chart { get; set; }
public ClientInfoViewModel(MainViewModel parent,MainWindowViewModel mainWindowViewModel, string username)
{
MainWindowViewModel = mainWindowViewModel;
this.parent = parent;
this.PatientInfo = new PatientInfo() { Username = username, Status = "Waiting to start" };
this.Chart = new Chart(this.PatientInfo);
PatientInfo.ChatLog = new ObservableCollection<string>();
client = mainWindowViewModel.client;
StartSession = new RelayCommand(() =>
{
client.sendMessage(DataParser.getStartSessionJson(PatientInfo.Username));
PatientInfo.Status = "Session started";
});
StopSession = new RelayCommand(() =>
{
client.sendMessage(DataParser.getStopSessionJson(PatientInfo.Username));
PatientInfo.Status = "Session stopped, waiting to start again.";
});
Chat = new RelayCommand<object>((parameter) =>
{
SendMessageToClient(PatientInfo.Username, ((TextBox)parameter).Text);
});
//TODO RelayCommand ChatToAll
ChatToAll = new RelayCommand<object>((parameter) =>
{
Debug.WriteLine("[CLIENTINFOVIEWMODEL] sending message to all clients");
this.parent?.SendToAllClients(((TextBox)parameter).Text);
});
SetResistance = new RelayCommand<object>((parameter) =>
{
Debug.WriteLine("resistance");
//client.sendMessage(DataParser.getSetResistanceJson(PatientInfo.Username, float.Parse(((TextBox)parameter).Text)));
PatientInfo.Resistance = float.Parse(((TextBox)parameter).Text);
});
}
public void SendMessageToClient(string username, string text)
{
client.sendMessage(DataParser.getChatJson(username, text));
PatientInfo.ChatLog.Add(DateTime.Now + ": " + text);
}
public void BPMData(byte [] bytes)
{
//TODO
//Parsen van de data you fuck
if(bytes[0] == 0x00)
{
}
else
{
PatientInfo.BPM = bytes[1];
if (MySelectedItem == "BPM")
{
Chart.NewValue(PatientInfo.BPM);
}
}
}
public void BikeData(byte[] bytes)
{
//TODO
//Parsen van de data you fuck
switch (bytes[0])
{
case 0x10:
if (bytes[1] != 25)
{
throw new Exception();
}
PatientInfo.Distance = bytes[3];
PatientInfo.Speed = (bytes[4] | (bytes[5] << 8)) * 0.01;
if (MySelectedItem == "Speed")
{
Chart.NewValue(PatientInfo.Speed);
}
break;
case 0x19:
PatientInfo.Acc_Power = bytes[3] | (bytes[4] << 8);
PatientInfo.Curr_Power = (bytes[5]) | (bytes[6] & 0b00001111) << 8;
break;
default:
Debug.WriteLine("rip");
break;
}
}
}
}

View File

@@ -0,0 +1,47 @@

using DoctorApp.Utils;
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Controls;
using System.Windows.Input;
using Util;
namespace DoctorApp.ViewModels
{
class LoginViewModel : ObservableObject
{
public string Username { get; set; }
public ICommand LoginCommand { get; set; }
public bool LoginStatus { get; set; }
public bool InvertedLoginStatus { get; set; }
private MainWindowViewModel mainWindowViewModel;
public LoginViewModel(MainWindowViewModel mainWindowViewModel)
{
this.mainWindowViewModel = mainWindowViewModel;
LoginCommand = new RelayCommand<object>((parameter) =>
{
//TODO send username and password to server
this.mainWindowViewModel.client.tryLogin(Username, ((PasswordBox)parameter).Password);
});
}
internal void setLoginStatus(bool status)
{
this.mainWindowViewModel.InfoModel.ConnectedToServer = status;
this.InvertedLoginStatus = !status;
if (status)
{
MainViewModel mainViewModel = new MainViewModel(mainWindowViewModel);
this.mainWindowViewModel.client.SetMainViewModel(mainViewModel);
this.mainWindowViewModel.SelectedViewModel = mainViewModel;
}
}
}
}

View File

@@ -0,0 +1,87 @@
using DoctorApp.Utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using System.Windows.Controls;
using Util;
namespace DoctorApp.ViewModels
{
class MainViewModel : ObservableObject
{
public ObservableCollection<ClientInfoViewModel> Tabs { get; set; }
public int Selected { get; set; }
public MainWindowViewModel MainWindowViewModel { get; set; }
Client client;
public MainViewModel(MainWindowViewModel mainWindowViewModel)
{
this.MainWindowViewModel = mainWindowViewModel;
client = this.MainWindowViewModel.client;
Tabs = new ObservableCollection<ClientInfoViewModel>();
}
public void NewConnectedUser(string username)
{
Debug.WriteLine("new tab with name " + username);
App.Current.Dispatcher.Invoke((Action)delegate
{
Tabs.Add(new ClientInfoViewModel(this,MainWindowViewModel, username));
});
}
public void DisconnectedUser(string username)
{
App.Current.Dispatcher.Invoke((Action)delegate
{
foreach (ClientInfoViewModel item in Tabs)
{
if (item.PatientInfo.Username == username)
{
Tabs.Remove(item);
break;
}
}
});
}
public void TransferDataToClientBike(byte[] bytes)
{
string username = DataParser.getNameFromBytesBike(bytes);
foreach(ClientInfoViewModel item in Tabs)
{
if(item.PatientInfo.Username == username)
{
item.BikeData(DataParser.getDataWithoutName(bytes,0,8));
}
}
}
public void TransferDataToClientBPM(byte[] bytes)
{
string username = DataParser.getNameFromBytesBPM(bytes);
foreach (ClientInfoViewModel item in Tabs)
{
if (item.PatientInfo.Username == username)
{
item.BikeData(DataParser.getDataWithoutName(bytes, 0,2));
}
}
}
internal void SendToAllClients(string text)
{
Debug.WriteLine("[MAINVIEWMODEL] Sending message to all clients: " + text);
foreach (ClientInfoViewModel item in Tabs)
{
item.SendMessageToClient(item.PatientInfo.Username, text);
}
}
}
}

View File

@@ -0,0 +1,135 @@
using GalaSoft.MvvmLight.Command;
using System.Diagnostics;
using Util;
using System.Windows;
using System.Windows.Input;
using DoctorApp.Models;
using DoctorApp.Utils;
using Util.MagicCode;
using System;
namespace DoctorApp.ViewModels
{
class MainWindowViewModel : ObservableObject
{
#region private members
private Window mWindow;
private int mOuterMarginSize = 10;
private int mWindowRadius = 10;
#endregion
#region commands
public ICommand MinimizeCommand { get; set; }
public ICommand MaximizeCommand { get; set; }
public ICommand CloseCommand { get; set; }
public ICommand MenuCommand { get; set; }
#endregion
#region public properties
public Info InfoModel { get; set; }
public ObservableObject SelectedViewModel { get; set; }
public Client client { get; }
/// <summary>
/// size of the resize border around the window
/// </summary>
public double MinimumWidth { get; set; } = 250;
public double MinimumHeight { get; set; } = 250;
public int ResizeBorder { get; set; } = 6;
public Thickness ResizeBorderThickness { get { return new Thickness(ResizeBorder + OuterMarginSize); } }
public Thickness InnerContentPadding { get { return new Thickness(ResizeBorder); } }
public Thickness OuterMarginThickness { get { return new Thickness(OuterMarginSize); } }
public CornerRadius WindowCornerRadius { get { return new CornerRadius(WindowRadius); } }
public int OuterMarginSize
{
get
{
return mWindow.WindowState == WindowState.Maximized ? 0 : mOuterMarginSize;
}
set
{
mOuterMarginSize = value;
}
}
public int WindowRadius
{
get
{
return mWindow.WindowState == WindowState.Maximized ? 0 : mWindowRadius;
}
set
{
mWindowRadius = value;
}
}
public int TitleHeight { get; set; } = 42;
public GridLength TitleHeightGridLength { get { return new GridLength(TitleHeight + ResizeBorder); } }
#endregion
public MainWindowViewModel(Window window, Client client)
{
this.mWindow = window;
this.mWindow.StateChanged += (sender, e) =>
{
OnPropertyChanged(nameof(ResizeBorderThickness));
OnPropertyChanged(nameof(OuterMarginThickness));
OnPropertyChanged(nameof(WindowCornerRadius));
OnPropertyChanged(nameof(OuterMarginSize));
OnPropertyChanged(nameof(WindowRadius));
};
this.InfoModel = new Info();
this.client = client;
LoginViewModel loginViewModel = new LoginViewModel(this);
SelectedViewModel = loginViewModel;
this.client.SetLoginViewModel(loginViewModel);
this.MinimizeCommand = new RelayCommand(() => this.mWindow.WindowState = WindowState.Minimized);
this.MaximizeCommand = new RelayCommand(() => this.mWindow.WindowState ^= WindowState.Maximized);
this.CloseCommand = new RelayCommand(() => this.mWindow.Close());
this.MenuCommand = new RelayCommand(() => SystemCommands.ShowSystemMenu(this.mWindow, GetMousePosition()));
var resizer = new WindowResizer(this.mWindow);
this.mWindow.Closed += (sender, e) => this.client.Dispose();
}
#region helper
private Point GetMousePosition()
{
Debug.WriteLine("getmousePosition called");
var p = Mouse.GetPosition(this.mWindow);
return new Point(p.X + this.mWindow.Left, p.Y + this.mWindow.Top);
}
#endregion
}
}

View File

@@ -0,0 +1,90 @@
<UserControl x:Class="DoctorApp.Views.ClientInfoView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DoctorApp.Views"
mc:Ignorable="d"
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Margin="15,5,15,15">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="43*"/>
<RowDefinition Height="47*"/>
<RowDefinition Height="180*"/>
<RowDefinition Height="180*"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Grid.RowSpan="2" Margin="0,0,0,22">
<StackPanel.Resources>
<Style TargetType="{x:Type Label}">
<Setter Property="Margin" Value="0,0,20,0"/>
</Style>
</StackPanel.Resources>
<Label Content="{Binding Path=PatientInfo.Username}"/>
<Label Content="{Binding Path=PatientInfo.Status}"/>
</StackPanel>
<StackPanel Margin="0,10,0,0" Grid.RowSpan="2" Grid.Row="1">
<StackPanel.Resources>
<Style TargetType="{x:Type DockPanel}">
<Setter Property="Margin" Value="0,20,0,0"/>
</Style>
</StackPanel.Resources>
<DockPanel Height="26" LastChildFill="False" HorizontalAlignment="Stretch">
<Label Content="Resistance" Width="110" DockPanel.Dock="Right"/>
<Label Content="Current Speed" Width="110" DockPanel.Dock="Left"/>
<Label Content="Current BPM" Width="110" DockPanel.Dock="Top"/>
</DockPanel>
<DockPanel Height="26" LastChildFill="False" HorizontalAlignment="Stretch">
<TextBox Name="textBox_Resistance" Text="{Binding Path=PatientInfo.Resistance}" TextWrapping="Wrap" Width="110" DockPanel.Dock="Right" IsReadOnly="true"/>
<TextBox Name="textBox_CurrentSpeed" Text="{Binding Path=PatientInfo.Speed}" TextWrapping="Wrap" Width="110" DockPanel.Dock="Left" IsReadOnly="true"/>
<TextBox Name="textBox_CurrentBPM" Text="{Binding Path=PatientInfo.BPM}" TextWrapping="Wrap" Width="110" DockPanel.Dock="Top" Height="26" IsReadOnly="true"/>
</DockPanel>
<DockPanel Height="26" LastChildFill="False">
<Label Content="Distance Covered" Width="110" DockPanel.Dock="Right"/>
<Label Content="Current Power" Width="110" DockPanel.Dock="Left"/>
<Label Content="Acc. Power" Width="110" DockPanel.Dock="Top"/>
</DockPanel>
<DockPanel Height="26" LastChildFill="False">
<TextBox Name="textBox_DistanceCovered" Text="{Binding Path=PatientInfo.Distance}" TextWrapping="Wrap" Width="110" DockPanel.Dock="Right" IsReadOnly="true"/>
<TextBox Name="textBox_CurrentPower" Text="{Binding Path=PatientInfo.Curr_Power}" TextWrapping="Wrap" Width="110" DockPanel.Dock="Left" IsReadOnly="true"/>
<TextBox Name="textBox_AccPower" Text="{Binding Path=PatientInfo.Acc_Power}" TextWrapping="Wrap" Width="110" DockPanel.Dock="Top" Height="26" IsReadOnly="true"/>
</DockPanel>
</StackPanel>
<ListBox Name="ChatBox" ItemsSource="{Binding PatientInfo.ChatLog}" Grid.Column="1" Margin="59,41,0,0" Grid.RowSpan="3"/>
<TextBox Name="textBox_Chat" Grid.Column="1" HorizontalAlignment="Left" Margin="59,10,0,0" Grid.Row="3" Text="" TextWrapping="Wrap" VerticalAlignment="Top" Width="235"/>
<Button x:Name="Send" Content="Send" Grid.Column="1" HorizontalAlignment="Left" Margin="59,33,0,0" Grid.Row="3" VerticalAlignment="Top" Command="{Binding Chat}" CommandParameter="{Binding ElementName=textBox_Chat}"/>
<Button x:Name="SendToAll" Content="Send to all clients" Grid.Column="1" HorizontalAlignment="Left" Margin="107,33,0,0" Grid.Row="3" VerticalAlignment="Top" Command="{Binding ChatToAll}" CommandParameter="{Binding ElementName=textBox_Chat}"/>
<Button Content="Start Session" Grid.Column="1" HorizontalAlignment="Left" Margin="69,86,0,0" Grid.Row="3" VerticalAlignment="Top" Width="97" Command="{Binding StartSession}" CommandParameter=""/>
<Button Content="Stop Session" Grid.Column="1" HorizontalAlignment="Left" Margin="187,86,0,0" Grid.Row="3" VerticalAlignment="Top" Width="97" Command="{Binding StopSession}" CommandParameter=""/>
<TextBox x:Name="textBox_SetResistance" Grid.Column="1" HorizontalAlignment="Left" Margin="69,128,0,0" Grid.Row="3" TextWrapping="Wrap" VerticalAlignment="Top" Width="97"/>
<Button Content="Set Resistance" Grid.Column="1" HorizontalAlignment="Left" Margin="187,128,0,0" Grid.Row="3" VerticalAlignment="Top" Width="97" Height="18" Command="{Binding SetResistance}" CommandParameter="{Binding ElementName=textBox_SetResistance}"/>
<lvc:CartesianChart Grid.Row="3" AnimationsSpeed="0:0:0.5" Hoverable="False" DataTooltip="{x:Null}" Margin="0,33,0,-5">
<lvc:CartesianChart.Series>
<lvc:LineSeries Values="{Binding Chart.ChartValues}"
PointGeometry="{x:Null}"
LineSmoothness="1"
StrokeThickness="6"
Stroke="#F34336"
Fill="Transparent"/>
</lvc:CartesianChart.Series>
<lvc:CartesianChart.AxisX>
<lvc:Axis LabelFormatter="{Binding Chart.DateTimeFormatter}"
MaxValue="{Binding Chart.AxisMax}"
MinValue="{Binding Chart.AxisMin}"
Unit="{Binding Chart.AxisUnit}">
<lvc:Axis.Separator>
<lvc:Separator Step="{Binding Chart.AxisStep}" />
</lvc:Axis.Separator>
</lvc:Axis>
</lvc:CartesianChart.AxisX>
</lvc:CartesianChart>
<ComboBox Name="DropBox" HorizontalAlignment="Left" Margin="0,6,0,0" Grid.Row="3" VerticalAlignment="Top" Width="190" SelectedItem="{Binding Path=MySelectedItem}">
<ComboBoxItem Content="Speed" IsSelected="True"></ComboBoxItem>
<ComboBoxItem Content="BPM"></ComboBoxItem>
</ComboBox>
</Grid>
</UserControl>

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DoctorApp.Views
{
/// <summary>
/// Interaction logic for ClientInfoView.xaml
/// </summary>
public partial class ClientInfoView : UserControl
{
public ClientInfoView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,25 @@
<UserControl x:Class="DoctorApp.Views.LoginView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DoctorApp.Views"
xmlns:viewModels="clr-namespace:DoctorApp.ViewModels"
xmlns:Util="clr-namespace:Util.MagicCode"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
>
<DockPanel>
<StackPanel VerticalAlignment="Center" Width="auto">
<Label Content="Username" HorizontalContentAlignment="Center" />
<TextBox x:Name="Username" Text="{Binding Username}" TextWrapping="Wrap" Width="120" Util:FocusAdvancement.AdvancesByEnterKey="True"/>
<Label Content="Password" HorizontalContentAlignment="Center"/>
<PasswordBox x:Name="Password" Width="120" Util:FocusAdvancement.AdvancesByEnterKey="True"/>
<Button x:Name="Login" Content="Login" Command="{Binding LoginCommand}" CommandParameter="{Binding ElementName=Password}" Margin="0,20,0,0" Width="120"/>
<Popup IsOpen="{Binding InvertedLoginStatus}" PopupAnimation = "Fade" HorizontalAlignment="Left">
<Label Content="Login failed" Foreground="Red" Background="#FFFF" />
</Popup>
</StackPanel>
</DockPanel>
</UserControl>

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DoctorApp.Views
{
/// <summary>
/// Interaction logic for LoginView.xaml
/// </summary>
public partial class LoginView : UserControl
{
public LoginView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,27 @@
<UserControl x:Class="DoctorApp.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DoctorApp.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TabControl TabStripPlacement="Left" ItemsSource="{Binding Tabs}" SelectedItem="{Binding Selected}">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="/img/patient.png" Width="25" Margin="0 0 5 0"/>
<TextBlock Text="{Binding PatientInfo.Username}" FontSize="20" MaxWidth="100" Width="100" TextTrimming="CharacterEllipsis"/>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl Content="{Binding}"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</UserControl>

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DoctorApp.Views
{
/// <summary>
/// Interaction logic for MainView.xaml
/// </summary>
public partial class MainView : UserControl
{
public MainView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,122 @@
<Window x:Class="DoctorApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DoctorApp"
mc:Ignorable="d"
Background="Transparent"
WindowStyle="None"
AllowsTransparency="True"
MinHeight="{Binding MinimumHeight}"
MinWidth="{Binding MinimumWidth}"
Title="MainWindow" Height="500" Width="900">
<Window.Resources>
<Style TargetType="{x:Type local:MainWindow}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Border Padding="{Binding OuterMarginThickness, FallbackValue=10}" >
<Grid>
<!-- opacity mask -->
<Border x:Name="Container"
Background="{StaticResource BackgroundLightBrush}"
CornerRadius="{Binding WindowCornerRadius, FallbackValue=10}"/>
<Border CornerRadius="{Binding WindowCornerRadius, FallbackValue=10}"
Background="{StaticResource BackgroundVeryLightBrush}">
<Border.Effect>
<DropShadowEffect ShadowDepth="0" Opacity="0.2"/>
</Border.Effect>
</Border>
<Grid>
<Grid.OpacityMask>
<VisualBrush Visual="{Binding ElementName=Container}"/>
</Grid.OpacityMask>
<Grid.RowDefinitions>
<RowDefinition Height="{Binding TitleHeightGridLength, FallbackValue=42}"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Title bar -->
<Grid Grid.Column="0" Grid.Row="0" Panel.ZIndex="1" Background="{StaticResource BackgroundLightBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- icon -->
<Button Grid.Column="0" Style="{StaticResource SystemIconButton}" Command="{Binding MenuCommand}">
<Image Source="/img/doctor.ico"/>
<!--<TextBlock Text="icon"/>-->
</Button>
<!-- Title -->
<Viewbox Grid.Column="1" Margin="0">
<TextBlock Style="{StaticResource HeaderText}" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Title, FallbackValue=failed}"/>
</Viewbox>
<!-- Window buttons -->
<StackPanel Grid.Column="2" Orientation="Horizontal">
<Button Style="{StaticResource WindowControlButton}" Content="_" Command="{Binding MinimizeCommand}"/>
<Button Style="{StaticResource WindowControlButton}" Content="[]" Command="{Binding MaximizeCommand}"/>
<Button Style="{StaticResource WindowCloseButton}" Content="X" Command="{Binding CloseCommand}"/>
</StackPanel>
</Grid>
<!-- Page content -->
<Border Grid.Row="1" Grid.Column="0">
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
<!-- shadow? -->
<Border Grid.Row="1" Height="6" BorderThickness="0 0.2 0 0" VerticalAlignment="Top">
<!--<Border.BorderBrush>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="{StaticResource BackgroundSemiLight}" Offset="0.0"/>
<GradientStop Color="{StaticResource BackgroundVeryLight}" Offset="1.0"/>
</LinearGradientBrush>
</Border.BorderBrush>-->
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="Transparent" Offset="1.0"/>
<GradientStop Color="#7000" Offset="0.0"/>
</LinearGradientBrush>
</Border.Background>
</Border>
</Grid>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<WindowChrome.WindowChrome>
<WindowChrome
ResizeBorderThickness="{Binding ResizeBorderThickness}"
CaptionHeight="{Binding TitleHeight}"
CornerRadius="0"
GlassFrameThickness="0"/>
</WindowChrome.WindowChrome>
<Grid>
<Frame Content="{Binding SelectedViewModel}" Focusable="False" NavigationUIVisibility="Hidden"/>
<Label Content="gemaakt door: mensen" DockPanel.Dock="Bottom" HorizontalAlignment="Right" VerticalAlignment="Bottom" FontStyle="Italic" Foreground="Gray"/>
</Grid>
</Window>

View File

@@ -0,0 +1,32 @@
using DoctorApp.Utils;
using DoctorApp.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DoctorApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
Client client = new Client();
InitializeComponent();
DataContext = new MainWindowViewModel(this, client);
}
}
}

BIN
DoctorApp/doctor.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

BIN
DoctorApp/img/doctor.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

BIN
DoctorApp/img/patient.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -21,7 +21,7 @@
<Label Content="Username" HorizontalContentAlignment="Center"/>
<TextBox x:Name="Username" TextWrapping="Wrap" Width="120"/>
<Label Content="Password" HorizontalContentAlignment="Center"/>
<TextBox x:Name="Password" TextWrapping="Wrap" Width="120"/>
<PasswordBox x:Name="Password" Width="120"/>
<Button x:Name="Login" Content="Login" Margin="0,20,0,0" Click="Login_Click_1" />
</StackPanel>

View File

@@ -28,24 +28,19 @@ namespace DokterApp
}
private void Login_Click_1(object sender, RoutedEventArgs e)
{
WindowTabs windowTabs = new WindowTabs();
handler = windowTabs.NewTab;
this.Label.Content = "Waiting";
this.client = new Client("localhost", 5555, this.Username.Text, this.Password.Text, handler);
this.client = new Client("localhost", 5555, this.Username.Text, this.Password.Password, handler);
while (!client.IsConnected())
{
}
windowTabs.Show();
this.Close();
}
}

471
Hashing/DataParser.cs Normal file
View File

@@ -0,0 +1,471 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Security.Cryptography;
using System.Text;
namespace Util
{
public class DataParser
{
public const string LOGIN = "LOGIN";
public const string LOGIN_RESPONSE = "LOGIN RESPONSE";
public const string START_SESSION = "START SESSION";
public const string STOP_SESSION = "STOP SESSION";
public const string SET_RESISTANCE = "SET RESISTANCE";
public const string NEW_CONNECTION = "NEW CONNECTION";
public const string DISCONNECT = "DISCONNECT";
public const string LOGIN_DOCTOR = "LOGIN DOCTOR";
public const string MESSAGE = "MESSAGE";
/// <summary>
/// makes the json object with LOGIN identifier and username and password
/// </summary>
/// <param name="mUsername">username</param>
/// <param name="mPassword">password</param>
/// <returns>json object to ASCII to bytes</returns>
public static byte[] GetLoginJson(string mUsername, string mPassword)
{
dynamic json = new
{
identifier = LOGIN,
data = new
{
username = mUsername,
password = mPassword,
}
};
return Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(json));
}
internal static string getNameFromBytesBike(byte[] bytes)
{
return getName(bytes, 8, bytes.Length - 8);
}
/// <summary>
/// converts the given string parameter into a message using our protocol.
/// </summary>
/// <param name="messageToSend">the message string to send</param>
/// <returns>a byte array using our protocol to send the message</returns>
public static byte[] GetMessageToSend(string messageToSend)
{
dynamic json = new
{
identifier = MESSAGE,
data = new
{
message = messageToSend
}
};
return Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(json));
}
internal static string getNameFromBytesBPM(byte[] bytes)
{
return getName(bytes, 2, bytes.Length - 2);
}
private static string getName(byte[] bytes , int offset, int lenght)
{
byte[] nameArray = new byte[lenght];
Array.Copy(bytes, offset, nameArray, 0, lenght);
return Encoding.UTF8.GetString(nameArray);
}
/// <summary>
/// creates a message for when the doctor wants to log in.
/// </summary>
/// <param name="mUsername">the username of the doctor</param>
/// <param name="mPassword">the (hashed) password of the doctor</param>
/// <returns>a byte array using our protocol that contains the username and password of the doctor</returns>
public static byte[] LoginAsDoctor(string mUsername, string mPassword)
{
dynamic json = new
{
identifier = LOGIN_DOCTOR,
data = new
{
username = mUsername,
password = mPassword,
}
};
return Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(json));
}
/// <summary>
/// gets the username and password from a given message array.
/// </summary>
/// <param name="jsonbytes">the array of bytes containing the message</param>
/// <param name="username">the username variable that the username will be put into</param>
/// <param name="password">the password variable that the password will be put into</param>
/// <returns><c>true</c> if the username and password were received correctly, <c>false</c> otherwise</returns>
public static bool GetUsernamePassword(byte[] jsonbytes, out string username, out string password)
{
dynamic json = JsonConvert.DeserializeObject(Encoding.ASCII.GetString(jsonbytes));
try
{
username = json.data.username;
password = json.data.password;
return true;
}
catch
{
username = null;
password = null;
return false;
}
}
/// <summary>
/// gets message using our protocol of the given identifier and data.
/// </summary>
/// <param name="mIdentifier">the identifier string of the message</param>
/// <param name="data">the payload data of the message</param>
/// <returns>a byte array containing the json message with the given parameters, using our protocol.</returns>
private static byte[] getJsonMessage(string mIdentifier, dynamic data)
{
dynamic json = new
{
identifier = mIdentifier,
data
};
return getMessage(Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(json)), 0x01);
}
/// <summary>
/// gets a message using our protocol with only the given identifier string.
/// </summary>
/// <param name="mIdentifier">the identifier to put into the message</param>
/// <returns>a byte array containing the json with only the identifier, using our protocol.</returns>
private static byte[] getJsonMessage(string mIdentifier)
{
dynamic json = new
{
identifier = mIdentifier,
};
return getMessage(Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(json)), 0x01);
}
/// <summary>
/// gets the login response of the given status
/// </summary>
/// <param name="mStatus">the status of the response</param>
/// <returns>a byte array containing the response for the given status, using our protocol.</returns>
public static byte[] getLoginResponse(string mStatus)
{
return getJsonMessage(LOGIN_RESPONSE, new { status = mStatus });
}
/// <summary>
/// gets the status of the given json message
/// </summary>
/// <param name="json">the byte array containing a json message using our protocol</param>
/// <returns>the response of the message</returns>
public static string getResponseStatus(byte[] json)
{
return ((dynamic)JsonConvert.DeserializeObject(Encoding.ASCII.GetString(json))).data.status;
}
/// <summary>
/// get the identifier from json
/// </summary>
/// <param name="bytes">json in ASCII</param>
/// <param name="identifier">gets the identifier</param>
/// <returns>if it sucseeded</returns>
public static bool getJsonIdentifier(byte[] bytes, out string identifier)
{
if (bytes.Length <= 5)
{
throw new ArgumentException("bytes to short");
}
byte messageId = bytes[4];
if (messageId == 0x01)
{
dynamic json = JsonConvert.DeserializeObject(Encoding.ASCII.GetString(bytes.Skip(5).ToArray()));
identifier = json.identifier;
return true;
}
else
{
identifier = "";
return false;
}
}
/// <summary>
/// checks if the de message is raw data according to the protocol
/// </summary>
/// <param name="bytes">message</param>
/// <returns>if message contains raw data</returns>
public static bool isRawDataBikeServer(byte[] bytes)
{
if (bytes.Length <= 5)
{
throw new ArgumentException("bytes to short");
}
return bytes[4] == 0x02;
}
public static bool isRawDataBPMServer(byte[] bytes)
{
if (bytes.Length <= 5)
{
throw new ArgumentException("bytes to short");
}
return bytes[4] == 0x03;
}
public static bool isRawDataBikeDoctor(byte[] bytes)
{
if (bytes.Length <= 5)
{
throw new ArgumentException("bytes to short");
}
return bytes[4] == 0x04;
}
public static bool isRawDataBPMDoctor(byte[] bytes)
{
if (bytes.Length <= 5)
{
throw new ArgumentException("bytes to short");
}
return bytes[4] == 0x05;
}
/// <summary>
/// constructs a message with the payload, messageId and clientId
/// </summary>
/// <param name="payload"></param>
/// <param name="messageId"></param>
/// <param name="clientId"></param>
/// <returns>the message ready for sending</returns>
private static byte[] getMessage(byte[] payload, byte messageId)
{
byte[] res = new byte[payload.Length + 5];
Array.Copy(BitConverter.GetBytes(payload.Length + 5), 0, res, 0, 4);
res[4] = messageId;
Array.Copy(payload, 0, res, 5, payload.Length);
return res;
}
/// <summary>
/// constructs a message with the payload and clientId and assumes the payload is raw data
/// </summary>
/// <param name="payload"></param>
/// <param name="clientId"></param>
/// <returns>the message ready for sending</returns>
public static byte[] GetRawBikeDataMessageServer(byte[] payload)
{
return getMessage(payload, 0x02);
}
public static byte[] GetRawBPMDataMessageServer(byte[] payload)
{
return getMessage(payload, 0x03);
}
public static byte[] GetRawBikeDataDoctor(byte[] payload, string username)
{
return GetRawDataDoctor(payload, username, 0x04);
}
public static byte[] GetRawBPMDataDoctor(byte[] payload, string username)
{
return GetRawDataDoctor(payload,username,0x05);
}
private static byte[] GetRawDataDoctor(byte[] payload, string username, byte messageID)
{
return getMessage(payload.Concat(Encoding.ASCII.GetBytes(username)).ToArray(), messageID);
}
/// <summary>
/// constructs a message with the payload and clientId and assumes the payload is json
/// </summary>
/// <param name="payload"></param>
/// <param name="clientId"></param>
/// <returns>the message ready for sending</returns>
public static byte[] getJsonMessage(byte[] payload)
{
return getMessage(payload, 0x01);
}
/// <summary>
/// gets the message to start a session with the given user username
/// </summary>
/// <param name="user">the username of the user we want to start the session for</param>
/// <returns>a byte array containing the message to start the session of the given user, using our protocol.</returns>
public static byte[] getStartSessionJson(string user)
{
dynamic data = new
{
username = user
};
return getJsonMessage(START_SESSION, data);
}
/// <summary>
/// gets the message to stop a session with the given user username
/// </summary>
/// <param name="user">the username of the user we want to stop the session for</param>
/// <returns>a byte array containing the message to stop the session of the given user, using our protocol.</returns>
public static byte[] getStopSessionJson(string user)
{
dynamic data = new
{
username = user
};
return getJsonMessage(STOP_SESSION, data);
}
/// <summary>
/// gets the message to set the resistance of the given user with the given resistance.
/// </summary>
/// <param name="user">the username to set the resistance of.</param>
/// <param name="mResistance">the resistance value to set</param>
/// <returns>a byte array containing a json messsage to set the user's resistance, using our protocol.</returns>
public static byte[] getSetResistanceJson(string user,float mResistance)
{
dynamic data = new
{
username = user,
resistance = mResistance
};
return getJsonMessage(SET_RESISTANCE, data);
}
/// <summary>
/// gets the response message with the given value.
/// </summary>
/// <param name="mWorked">the boolean value to indicate if the operation we want to send a response for was successful or not.</param>
/// <returns>a byte array containing a json message with the response and the given value.</returns>
public static byte[] getSetResistanceResponseJson(bool mWorked)
{
dynamic data = new
{
worked = mWorked
};
return getJsonMessage(SET_RESISTANCE, data);
}
/// <summary>
/// gets the message to indicate a new connection for the given user.
/// </summary>
/// <param name="user">the username of the user to start a connection for.</param>
/// <returns>a byte array containing a json message to indicate a new connection for the given user, using our protocol.</returns>
public static byte[] getNewConnectionJson(string user)
{
if (user == null)
throw new ArgumentNullException("user null");
dynamic data = new
{
username = user
};
return getJsonMessage(NEW_CONNECTION, data);
}
/// <summary>
/// gets the message for when a user has been disconnected.
/// </summary>
/// <param name="user">the username of the user that has been disconnected</param>
/// <returns>a byte array containing a json message to indicate that the given user has disconnected, using our protocol.</returns>
public static byte[] getDisconnectJson(string user)
{
dynamic data = new
{
username = user
};
return getJsonMessage(DISCONNECT, data);
}
/// <summary>
/// gets the resistance from the given json message
/// </summary>
/// <param name="json">the json messag</param>
/// <returns>the resistance that was in the message</returns>
public static float getResistanceFromJson(byte[] json)
{
return ((dynamic)JsonConvert.DeserializeObject(Encoding.ASCII.GetString(json))).data.resistance;
}
/// <summary>
/// gets the resistance response from the given json message
/// </summary>
/// <param name="json">the byte array containin the json message</param>
/// <returns>the response of the message, so wether it was successful or not.</returns>
public static bool getResistanceFromResponseJson(byte[] json)
{
Debug.WriteLine("got message " + Encoding.ASCII.GetString(json));
return ((dynamic)JsonConvert.DeserializeObject(Encoding.ASCII.GetString(json))).data.worked;
}
/// <summary>
/// gets the username from the given response message.
/// </summary>
/// <param name="json">the byte array containin the json message</param>
/// <returns>the username in the message.</returns>
public static string getUsernameFromResponseJson(byte[] json)
{
return ((dynamic)JsonConvert.DeserializeObject(Encoding.ASCII.GetString(json))).data.username;
}
/// <summary>
/// gets the chat message from the given json message.
/// </summary>
/// <param name="json">the byte array containin the json message</param>
/// <returns>the chat message in the json message</returns>
public static string getChatMessageFromJson(byte[] json)
{
return ((dynamic)JsonConvert.DeserializeObject(Encoding.ASCII.GetString(json))).data.chat;
}
/// <summary>
/// gets the username from the given json message.
/// </summary>
/// <param name="json">the byte array containin the json message</param>
/// <returns>the username that is in the message</returns>
public static string getUsernameFromJson(byte[] json)
{
return ((dynamic)JsonConvert.DeserializeObject(Encoding.ASCII.GetString(json))).data.username;
}
/// <summary>
/// gets the byte array with the json message to send a message with the given parameters.
/// </summary>
/// <param name="user">the username of the user that wants to send the message</param>
/// <param name="message">the message the user wants to send</param>
/// <returns>a byte array containing a json message with the username and corresponding message, using our protocol.</returns>
public static byte[] getChatJson(string user, string message)
{
dynamic data = new
{
username = user,
chat = message
};
return getJsonMessage(MESSAGE, data);
}
public static byte[] getDataWithoutName(byte[] bytes, int offset, int length)
{
byte[] data = new byte[length];
Array.Copy(bytes, offset, data, 0, length);
return data;
}
}
}

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
namespace Hashing
namespace Util
{
class Hasher
{

View File

@@ -9,6 +9,32 @@
<Import_RootNamespace>Hashing</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)DataParser.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Hasher.cs" />
<Compile Include="$(MSBuildThisFileDirectory)WPFStuff\MagicCode\FocusAdvancement.cs" />
<Compile Include="$(MSBuildThisFileDirectory)WPFStuff\MagicCode\WindowResizer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)WPFStuff\ObservableObject.cs" />
</ItemGroup>
<ItemGroup>
<Page Include="$(MSBuildThisFileDirectory)WPFStuff\Styles\Buttons.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)WPFStuff\Styles\Colors.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)WPFStuff\Styles\Fonts.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)WPFStuff\Styles\Texts.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)WPFStuff\Styles\Windows.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,65 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ClientApp">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Colors.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type Button}" x:Key="Hoverless">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}">
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type Button}" x:Key="SystemIconButton" BasedOn="{StaticResource Hoverless}" >
<Setter Property="WindowChrome.IsHitTestVisibleInChrome" Value="True"/>
<Setter Property="Padding" Value="4"/>
</Style>
<Style TargetType="{x:Type Button}" x:Key="WindowControlButton">
<Setter Property="WindowChrome.IsHitTestVisibleInChrome" Value="True"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="Foreground" Value="{StaticResource ForegroundMainBrush}"/>
<Setter Property="Padding" Value="6"/>
<Setter Property="LayoutTransform">
<Setter.Value>
<ScaleTransform ScaleX="2"/>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}">
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{StaticResource BackgroundSemiLightBrush}"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Button}" x:Key="WindowCloseButton" BasedOn="{StaticResource WindowControlButton}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,25 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ClientApp">
<Color x:Key="BackgroundLight">#efefef</Color>
<SolidColorBrush x:Key="BackgroundLightBrush" Color="{StaticResource BackgroundLight}"/>
<Color x:Key="BackgroundVeryLight">#fafafa</Color>
<SolidColorBrush x:Key="BackgroundVeryLightBrush" Color="{StaticResource BackgroundVeryLight}"/>
<Color x:Key="BackgroundSemiLight">#d7d7d7</Color>
<SolidColorBrush x:Key="BackgroundSemiLightBrush" Color="{StaticResource BackgroundSemiLight}"/>
<Color x:Key="ForegroundMain">#686868</Color>
<SolidColorBrush x:Key="ForegroundMainBrush" Color="{StaticResource ForegroundMain}"/>
<Color x:Key="ForegroundVeryDark">#000</Color>
<SolidColorBrush x:Key="ForegroundVeryDarkBrush" Color="{StaticResource ForegroundVeryDark}"/>
<Color x:Key="ForegroundWhite">#fff</Color>
<SolidColorBrush x:Key="ForegroundWhiteBrush" Color="{StaticResource ForegroundWhite}"/>
</ResourceDictionary>

View File

@@ -0,0 +1,5 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ClientApp">
</ResourceDictionary>

11
Hashing/Styles/Texts.xaml Normal file
View File

@@ -0,0 +1,11 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ClientApp">
<Style TargetType="{x:Type TextBlock}" x:Key="HeaderText">
<Setter Property="Foreground" Value="{StaticResource ForegroundMainBrush}"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Margin" Value="0 4"/>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,5 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ClientApp">
</ResourceDictionary>

View File

@@ -0,0 +1,40 @@
using System;
using System.Windows;
using System.Windows.Input;
namespace Util.MagicCode
{
public static class FocusAdvancement
{
public static bool GetAdvancesByEnterKey(DependencyObject obj)
{
return (bool)obj.GetValue(AdvancesByEnterKeyProperty);
}
public static void SetAdvancesByEnterKey(DependencyObject obj, bool value)
{
obj.SetValue(AdvancesByEnterKeyProperty, value);
}
public static readonly DependencyProperty AdvancesByEnterKeyProperty =
DependencyProperty.RegisterAttached("AdvancesByEnterKey", typeof(bool), typeof(FocusAdvancement),
new UIPropertyMetadata(OnAdvancesByEnterKeyPropertyChanged));
static void OnAdvancesByEnterKeyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var element = d as UIElement;
if (element == null) return;
if ((bool)e.NewValue) element.KeyDown += Keydown;
else element.KeyDown -= Keydown;
}
static void Keydown(object sender, KeyEventArgs e)
{
if (!e.Key.Equals(Key.Enter)) return;
var element = sender as UIElement;
if (element != null) element.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
}

View File

@@ -0,0 +1,212 @@
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace Util.MagicCode
{
/// <summary>
/// Fixes the issue with Windows of Style <see cref="WindowStyle.None"/> covering the taskbar
/// </summary>
public class WindowResizer
{
#region Private Members
/// <summary>
/// The window to handle the resizing for
/// </summary>
private Window mWindow;
#endregion
#region Dll Imports
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetCursorPos(out POINT lpPoint);
[DllImport("user32.dll")]
static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr MonitorFromPoint(POINT pt, MonitorOptions dwFlags);
#endregion
#region Constructor
/// <summary>
/// Default constructor
/// </summary>
/// <param name="window">The window to monitor and correctly maximize</param>
/// <param name="adjustSize">The callback for the host to adjust the maximum available size if needed</param>
public WindowResizer(Window window)
{
mWindow = window;
// Listen out for source initialized to setup
mWindow.SourceInitialized += Window_SourceInitialized;
}
#endregion
#region Initialize
/// <summary>
/// Initialize and hook into the windows message pump
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Window_SourceInitialized(object sender, System.EventArgs e)
{
// Get the handle of this window
var handle = (new WindowInteropHelper(mWindow)).Handle;
var handleSource = HwndSource.FromHwnd(handle);
// If not found, end
if (handleSource == null)
return;
// Hook into it's Windows messages
handleSource.AddHook(WindowProc);
}
#endregion
#region Windows Proc
/// <summary>
/// Listens out for all windows messages for this window
/// </summary>
/// <param name="hwnd"></param>
/// <param name="msg"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <param name="handled"></param>
/// <returns></returns>
private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
// Handle the GetMinMaxInfo of the Window
case 0x0024:/* WM_GETMINMAXINFO */
WmGetMinMaxInfo(hwnd, lParam);
handled = true;
break;
}
return (IntPtr)0;
}
#endregion
/// <summary>
/// Get the min/max window size for this window
/// Correctly accounting for the taskbar size and position
/// </summary>
/// <param name="hwnd"></param>
/// <param name="lParam"></param>
private void WmGetMinMaxInfo(System.IntPtr hwnd, System.IntPtr lParam)
{
POINT lMousePosition;
GetCursorPos(out lMousePosition);
IntPtr lPrimaryScreen = MonitorFromPoint(new POINT(0, 0), MonitorOptions.MONITOR_DEFAULTTOPRIMARY);
MONITORINFO lPrimaryScreenInfo = new MONITORINFO();
if (GetMonitorInfo(lPrimaryScreen, lPrimaryScreenInfo) == false)
{
return;
}
IntPtr lCurrentScreen = MonitorFromPoint(lMousePosition, MonitorOptions.MONITOR_DEFAULTTONEAREST);
MINMAXINFO lMmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));
if (lPrimaryScreen.Equals(lCurrentScreen) == true)
{
lMmi.ptMaxPosition.X = lPrimaryScreenInfo.rcWork.Left;
lMmi.ptMaxPosition.Y = lPrimaryScreenInfo.rcWork.Top;
lMmi.ptMaxSize.X = lPrimaryScreenInfo.rcWork.Right - lPrimaryScreenInfo.rcWork.Left;
lMmi.ptMaxSize.Y = lPrimaryScreenInfo.rcWork.Bottom - lPrimaryScreenInfo.rcWork.Top;
}
else
{
lMmi.ptMaxPosition.X = lPrimaryScreenInfo.rcMonitor.Left;
lMmi.ptMaxPosition.Y = lPrimaryScreenInfo.rcMonitor.Top;
lMmi.ptMaxSize.X = lPrimaryScreenInfo.rcMonitor.Right - lPrimaryScreenInfo.rcMonitor.Left;
lMmi.ptMaxSize.Y = lPrimaryScreenInfo.rcMonitor.Bottom - lPrimaryScreenInfo.rcMonitor.Top;
}
// Now we have the max size, allow the host to tweak as needed
Marshal.StructureToPtr(lMmi, lParam, true);
}
}
#region Dll Helper Structures
enum MonitorOptions : uint
{
MONITOR_DEFAULTTONULL = 0x00000000,
MONITOR_DEFAULTTOPRIMARY = 0x00000001,
MONITOR_DEFAULTTONEAREST = 0x00000002
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class MONITORINFO
{
public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));
public Rectangle rcMonitor = new Rectangle();
public Rectangle rcWork = new Rectangle();
public int dwFlags = 0;
}
[StructLayout(LayoutKind.Sequential)]
public struct Rectangle
{
public int Left, Top, Right, Bottom;
public Rectangle(int left, int top, int right, int bottom)
{
this.Left = left;
this.Top = top;
this.Right = right;
this.Bottom = bottom;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct MINMAXINFO
{
public POINT ptReserved;
public POINT ptMaxSize;
public POINT ptMaxPosition;
public POINT ptMinTrackSize;
public POINT ptMaxTrackSize;
};
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
/// <summary>
/// x coordinate of point.
/// </summary>
public int X;
/// <summary>
/// y coordinate of point.
/// </summary>
public int Y;
/// <summary>
/// Construct a point of coordinates (x,y).
/// </summary>
public POINT(int x, int y)
{
this.X = x;
this.Y = y;
}
}
#endregion
}

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
namespace Util
{
public abstract class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
public void OnPropertyChanged(string name)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}

View File

@@ -0,0 +1,65 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ClientApp">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Colors.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type Button}" x:Key="Hoverless">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}">
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type Button}" x:Key="SystemIconButton" BasedOn="{StaticResource Hoverless}" >
<Setter Property="WindowChrome.IsHitTestVisibleInChrome" Value="True"/>
<Setter Property="Padding" Value="4"/>
</Style>
<Style TargetType="{x:Type Button}" x:Key="WindowControlButton">
<Setter Property="WindowChrome.IsHitTestVisibleInChrome" Value="True"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="Foreground" Value="{StaticResource ForegroundMainBrush}"/>
<Setter Property="Padding" Value="6"/>
<Setter Property="LayoutTransform">
<Setter.Value>
<ScaleTransform ScaleX="2"/>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}">
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{StaticResource BackgroundSemiLightBrush}"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Button}" x:Key="WindowCloseButton" BasedOn="{StaticResource WindowControlButton}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,25 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ClientApp">
<Color x:Key="BackgroundLight">#efefef</Color>
<SolidColorBrush x:Key="BackgroundLightBrush" Color="{StaticResource BackgroundLight}"/>
<Color x:Key="BackgroundVeryLight">#fafafa</Color>
<SolidColorBrush x:Key="BackgroundVeryLightBrush" Color="{StaticResource BackgroundVeryLight}"/>
<Color x:Key="BackgroundSemiLight">#d7d7d7</Color>
<SolidColorBrush x:Key="BackgroundSemiLightBrush" Color="{StaticResource BackgroundSemiLight}"/>
<Color x:Key="ForegroundMain">#686868</Color>
<SolidColorBrush x:Key="ForegroundMainBrush" Color="{StaticResource ForegroundMain}"/>
<Color x:Key="ForegroundVeryDark">#000</Color>
<SolidColorBrush x:Key="ForegroundVeryDarkBrush" Color="{StaticResource ForegroundVeryDark}"/>
<Color x:Key="ForegroundWhite">#fff</Color>
<SolidColorBrush x:Key="ForegroundWhiteBrush" Color="{StaticResource ForegroundWhite}"/>
</ResourceDictionary>

View File

@@ -0,0 +1,5 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ClientApp">
</ResourceDictionary>

View File

@@ -0,0 +1,11 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ClientApp">
<Style TargetType="{x:Type TextBlock}" x:Key="HeaderText">
<Setter Property="Foreground" Value="{StaticResource ForegroundMainBrush}"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Margin" Value="0 4"/>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,5 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ClientApp">
</ResourceDictionary>

View File

@@ -44,19 +44,20 @@ namespace Hardware
/// </summary>
public void Connect()
{
BLE bleBike = new BLE();
Thread.Sleep(1000); // We need some time to list available devices
// List available devices
List<String> bleBikeList = bleBike.ListDevices();
Console.WriteLine("Devices found: ");
Console.WriteLine("[BLEHANDLER] Devices found: ");
foreach (var name in bleBikeList)
{
Console.WriteLine(name);
if (name.Contains("Avans Bike"))
{
Console.WriteLine("connecting to {0}", name);
Console.WriteLine("[BLEHANDLER] connecting to {0}", name);
Connect(name);
break;
@@ -202,5 +203,11 @@ namespace Hardware
bleBike.WriteCharacteristic("6e40fec3-b5a3-f393-e0a9-e50e24dcca9e", antMessage);
}
public void stop()
{
bleBike.SubscriptionValueChanged -= BleBike_SubscriptionValueChanged;
bleHeart.SubscriptionValueChanged -= BleBike_SubscriptionValueChanged;
}
}
}

View File

@@ -2,6 +2,7 @@
using ProftaakRH;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
@@ -28,6 +29,8 @@ namespace Hardware.Simulators
byte[] powerArray;
byte[] accPowerArray;
bool running = false;
public BikeSimulator(IDataReceiver dataReceiver)
@@ -47,15 +50,18 @@ namespace Hardware.Simulators
public void StartSimulation()
{
Console.WriteLine("simulating bike...");
//Example BLE Message
//4A-09-4E-05-19-16-00-FF-28-00-00-20-F0
this.running = true;
float x = 0.0f;
//Perlin for Random values
ImprovedPerlin improvedPerlin = new ImprovedPerlin(0, LibNoise.NoiseQuality.Best);
while (true)
while (this.running)
{
CalculateVariables(improvedPerlin.GetValue(x) + 1);
@@ -87,9 +93,25 @@ namespace Hardware.Simulators
//Generate an ANT message for page 0x10
private byte[] GenerateBike0x10()
{
byte[] bikeByte = { 0x10, Convert.ToByte(equipmentType), Convert.ToByte(elapsedTime * 4 % 64), Convert.ToByte(distanceTraveled), speedArray[0], speedArray[1], Convert.ToByte(BPM), 0xFF };
try
{
byte[] bikeByte = { 0x10, check(equipmentType), check(elapsedTime * 4 % 64), check((int)Math.Round(distanceTraveled)), speedArray[0], speedArray[1], check(BPM), 0xFF };
return bikeByte;
}
catch (OverflowException e)
{
Debug.WriteLine(e);
byte[] res = { 0x10,0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0xFF};
return res;
}
}
private byte check(int value)
{
return value > 255 ? Convert.ToByte(255) : value < 0 ? Convert.ToByte(0) : Convert.ToByte(value);
}
//Generate an ANT message for BPM
private byte[] GenerateHeart()
@@ -123,6 +145,10 @@ namespace Hardware.Simulators
this.resistance = (byte)Math.Max(Math.Min(Math.Round(percentage / 0.5), 255), 0);
}
public void stop()
{
this.running = false;
}
}
}

View File

@@ -7,5 +7,7 @@ namespace ProftaakRH
public interface IHandler
{
void setResistance(float percentage);
void stop();
}
}

View File

@@ -3,56 +3,50 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30413.136
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProftaakRH", "ProftaakRH.csproj", "{0F053CC5-D969-4970-9501-B3428EA3D777}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClientApp", "..\ClientApp\ClientApp.csproj", "{7EF854C1-73EB-4099-A7D7-057CCEEE6F8F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RH-Engine", "..\RH-Engine\RH-Engine.csproj", "{984E295E-47A2-41E7-90E5-50FDB9E67694}"
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Util", "..\Hashing\Util.shproj", "{70277749-D423-4871-B692-2EFC5A6ED932}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server", "..\Server\Server.csproj", "{B1AB6F51-A20D-4162-9A7F-B3350B7510FD}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProftaakRH", "ProftaakRH.csproj", "{C1A3CCE4-5FBB-4655-BFE1-7AF2B7D58CA3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "..\Client\Client.csproj", "{5759DD20-7A4F-4D8D-B986-A70A7818C112}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RH-Engine", "..\RH-Engine\RH-Engine.csproj", "{BECC2E56-E65C-42A0-AF80-DDE32DCD5E0B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Message", "..\Message\Message.csproj", "{9ED6832D-B0FB-4460-9BCD-FAA58863B0CE}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server", "..\Server\Server.csproj", "{7D751284-17E8-434C-A7F6-2EB37572E7AE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DokterApp", "..\DokterApp\DokterApp.csproj", "{B150F08B-13DA-4D17-BD96-7E89F52727C6}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Hashing", "..\Hashing\Hashing.shproj", "{70277749-D423-4871-B692-2EFC5A6ED932}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DoctorApp", "..\DoctorApp\DoctorApp.csproj", "{A232F2D5-AF98-4777-BF3A-FBDDFBC02994}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
..\Hashing\Hashing.projitems*{5759dd20-7a4f-4d8d-b986-a70a7818c112}*SharedItemsImports = 5
..\Hashing\Hashing.projitems*{70277749-d423-4871-b692-2efc5a6ed932}*SharedItemsImports = 13
..\Hashing\Hashing.projitems*{b150f08b-13da-4d17-bd96-7e89f52727c6}*SharedItemsImports = 5
..\Hashing\Hashing.projitems*{b1ab6f51-a20d-4162-9a7f-b3350b7510fd}*SharedItemsImports = 5
..\Hashing\Hashing.projitems*{7d751284-17e8-434c-a7f6-2eb37572e7ae}*SharedItemsImports = 5
..\Hashing\Hashing.projitems*{7ef854c1-73eb-4099-a7d7-057cceee6f8f}*SharedItemsImports = 5
..\Hashing\Hashing.projitems*{a232f2d5-af98-4777-bf3a-fbddfbc02994}*SharedItemsImports = 5
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0F053CC5-D969-4970-9501-B3428EA3D777}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0F053CC5-D969-4970-9501-B3428EA3D777}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0F053CC5-D969-4970-9501-B3428EA3D777}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0F053CC5-D969-4970-9501-B3428EA3D777}.Release|Any CPU.Build.0 = Release|Any CPU
{984E295E-47A2-41E7-90E5-50FDB9E67694}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{984E295E-47A2-41E7-90E5-50FDB9E67694}.Debug|Any CPU.Build.0 = Debug|Any CPU
{984E295E-47A2-41E7-90E5-50FDB9E67694}.Release|Any CPU.ActiveCfg = Release|Any CPU
{984E295E-47A2-41E7-90E5-50FDB9E67694}.Release|Any CPU.Build.0 = Release|Any CPU
{B1AB6F51-A20D-4162-9A7F-B3350B7510FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B1AB6F51-A20D-4162-9A7F-B3350B7510FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B1AB6F51-A20D-4162-9A7F-B3350B7510FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B1AB6F51-A20D-4162-9A7F-B3350B7510FD}.Release|Any CPU.Build.0 = Release|Any CPU
{5759DD20-7A4F-4D8D-B986-A70A7818C112}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5759DD20-7A4F-4D8D-B986-A70A7818C112}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5759DD20-7A4F-4D8D-B986-A70A7818C112}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5759DD20-7A4F-4D8D-B986-A70A7818C112}.Release|Any CPU.Build.0 = Release|Any CPU
{9ED6832D-B0FB-4460-9BCD-FAA58863B0CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9ED6832D-B0FB-4460-9BCD-FAA58863B0CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9ED6832D-B0FB-4460-9BCD-FAA58863B0CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9ED6832D-B0FB-4460-9BCD-FAA58863B0CE}.Release|Any CPU.Build.0 = Release|Any CPU
{B150F08B-13DA-4D17-BD96-7E89F52727C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B150F08B-13DA-4D17-BD96-7E89F52727C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B150F08B-13DA-4D17-BD96-7E89F52727C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B150F08B-13DA-4D17-BD96-7E89F52727C6}.Release|Any CPU.Build.0 = Release|Any CPU
{7EF854C1-73EB-4099-A7D7-057CCEEE6F8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7EF854C1-73EB-4099-A7D7-057CCEEE6F8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7EF854C1-73EB-4099-A7D7-057CCEEE6F8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7EF854C1-73EB-4099-A7D7-057CCEEE6F8F}.Release|Any CPU.Build.0 = Release|Any CPU
{C1A3CCE4-5FBB-4655-BFE1-7AF2B7D58CA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C1A3CCE4-5FBB-4655-BFE1-7AF2B7D58CA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C1A3CCE4-5FBB-4655-BFE1-7AF2B7D58CA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C1A3CCE4-5FBB-4655-BFE1-7AF2B7D58CA3}.Release|Any CPU.Build.0 = Release|Any CPU
{BECC2E56-E65C-42A0-AF80-DDE32DCD5E0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BECC2E56-E65C-42A0-AF80-DDE32DCD5E0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BECC2E56-E65C-42A0-AF80-DDE32DCD5E0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BECC2E56-E65C-42A0-AF80-DDE32DCD5E0B}.Release|Any CPU.Build.0 = Release|Any CPU
{7D751284-17E8-434C-A7F6-2EB37572E7AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7D751284-17E8-434C-A7F6-2EB37572E7AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7D751284-17E8-434C-A7F6-2EB37572E7AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7D751284-17E8-434C-A7F6-2EB37572E7AE}.Release|Any CPU.Build.0 = Release|Any CPU
{A232F2D5-AF98-4777-BF3A-FBDDFBC02994}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A232F2D5-AF98-4777-BF3A-FBDDFBC02994}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A232F2D5-AF98-4777-BF3A-FBDDFBC02994}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A232F2D5-AF98-4777-BF3A-FBDDFBC02994}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -19,30 +19,32 @@ namespace RH_Engine
this.tunnelID = tunnelID;
}
public string TerrainCommand(int[] sizeArray, float[] heightsArray)
public string TerrainAdd(int[] sizeArray, float[] heightsArray, string serialCode)
{
dynamic payload = new
{
id = "scene/terrain/add",
serial = serialCode,
data = new
{
size = sizeArray,
heights = heightsArray
heights = heightsArray,
}
};
return JsonConvert.SerializeObject(Payload(payload));
}
public string AddLayer(string uid, string texture)
public string AddLayer(string uuid, string serialCode)
{
dynamic payload = new
{
id = "scene/node/addlayer",
serial = serialCode,
data = new
{
id = uid,
diffuse = @"C:\Users\woute\Downloads\NetworkEngine.18.10.10.1\NetworkEngine\data\NetworkEngine\textures\terrain\adesert_cracks_d.jpg",
normal = @"C:\Users\woute\Downloads\NetworkEngine.18.10.10.1\NetworkEngine\data\NetworkEngine\textures\terrain\adesert_mntn_d.jpg",
id = uuid,
diffuse = @"data\NetworkEngine\textures\terrain\grass_green_d.jpg",
normal = @"data\NetworkEngine\textures\terrain\grass_green_n.jpg",
minHeight = 0,
maxHeight = 10,
fadeDist = 1
@@ -63,19 +65,27 @@ namespace RH_Engine
return JsonConvert.SerializeObject(Payload(payload));
}
public string AddNodeCommand()
public string renderTerrain(string serialCode)
{
dynamic payload = new
{
id = "scene/node/add",
serial = serialCode,
data = new
{
name = "newNode",
components = new
{
transform = new
{
position = new int[] { -80, 0, -80 },
scale = 1f,
rotation = new int[] { 0, 0, 0 }
},
terrain = new
{
smoothnormals = true
smoothnormals = true,
}
}
}
@@ -106,8 +116,23 @@ namespace RH_Engine
data = new
{
name = "dashboard",
parent = uuidBike,
components = new
{
transform = new
{
position = new float[]
{
-1.5f, 1f, 0f
//0f,0f,0f
},
scale = 1,
rotation = new int[]
{
-30, 90,0
//0,0,0
}
},
panel = new
{
size = new int[] { 1, 1 },
@@ -130,7 +155,7 @@ namespace RH_Engine
data = new
{
id = uuidPanel,
color = new int[] { 1, 1, 1, 1 }
color = new float[] { 0f, 0f, 0f, 0f }
}
};
@@ -151,7 +176,7 @@ namespace RH_Engine
return JsonConvert.SerializeObject(Payload(payload));
}
public string bikeSpeed(string uuidPanel, string serialCode, double speed)
private string showOnPanel(string uuidPanel, string serialCode, string mText, int index)
{
dynamic payload = new
{
@@ -160,9 +185,9 @@ namespace RH_Engine
data = new
{
id = uuidPanel,
text = "Speed: " + speed.ToString(),
position = new int[] { 4, 24 },
size = 36.0,
text = mText,
position = new int[] { 4, 24 + index * 32 },
size = 32.0,
color = new int[] { 0, 0, 0, 1 },
font = "segoeui"
}
@@ -171,6 +196,32 @@ namespace RH_Engine
return JsonConvert.SerializeObject(Payload(payload));
}
public string showBikespeed(string uuidPanel, string serialCode, double speed)
{
return showOnPanel(uuidPanel, serialCode, $"Speed: {string.Format("{0:.##}", speed)} m /s ({string.Format("{0:.##}", speed * 3.6)} km/h)", 0);
}
public string showHeartrate(string uuidPanel, string serialCode, int bpm)
{
return showOnPanel(uuidPanel, serialCode, "Heartrate: " + bpm + " bpm", 1);
}
public string showPower(string uuidPanel, string serialCode, double power)
{
return showOnPanel(uuidPanel, serialCode, "Inst. Power: " + power + " W", 2);
}
public string showResistance(string uuidPanel, string serialCode, double resistance)
{
return showOnPanel(uuidPanel, serialCode, "Resistance: " + resistance + " %", 3);
}
public string showMessage(string uuidPanel, string serialCode, string message)
{
return showOnPanel(uuidPanel, serialCode, "Last message: " + message, 5);
}
public string SwapPanelCommand(string uuid)
{
dynamic payload = new
@@ -204,6 +255,11 @@ namespace RH_Engine
return AddModel("bike", serial, "data\\NetworkEngine\\models\\bike\\bike.fbx");
}
public string AddBikeModelAnim(string serial, float scalar)
{
return AddModel("bike", serial, "data\\NetworkEngine\\models\\bike\\bike_anim.fbx", "Armature|Fietsen", new float[] { 0, 0, 0 }, scalar, new float[] { 0, 0, 0 });
}
public string AddModel(string nodeName, string serial, string fileLocation)
{
return AddModel(nodeName, serial, fileLocation, null, new float[] { 0, 0, 0 }, 1, new float[] { 0, 0, 0 });
@@ -277,6 +333,19 @@ namespace RH_Engine
return JsonConvert.SerializeObject(Payload(payload));
}
public string ShowRoute(string serialCode, bool goShow)
{
dynamic payload = new
{
id = "route/show",
data = new
{
show = goShow
}
};
return JsonConvert.SerializeObject(Payload(payload));
}
public string RouteCommand(string serialToSend)
{
ImprovedPerlin improvedPerlin = new ImprovedPerlin(4325, LibNoise.NoiseQuality.Best);
@@ -374,12 +443,27 @@ namespace RH_Engine
return JsonConvert.SerializeObject(Payload(payload));
}
public string RoadCommand(string uuid_route)
public string RouteSpeed(float speedValue,string nodeID)
{
dynamic payload = new
{
id = "route/follow/speed",
data = new
{
node = nodeID,
speed = speedValue
}
};
return JsonConvert.SerializeObject(Payload(payload));
}
public string RoadCommand(string uuid_route, string serialCode)
{
Console.WriteLine("road");
dynamic payload = new
{
id = "scene/road/add",
serial = serialCode,
data = new
{
route = uuid_route,
@@ -416,6 +500,7 @@ namespace RH_Engine
public string SkyboxCommand(double timeToSet)
{
Console.WriteLine(timeToSet);
if (timeToSet < 0 || timeToSet > 24)
{
throw new Exception("The time must be between 0 and 24!");

View File

@@ -1,4 +1,5 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
namespace RH_Engine
@@ -73,6 +74,7 @@ namespace RH_Engine
public static string GetID(string json)
{
//TODO fix null
dynamic d = JsonConvert.DeserializeObject(json);
return d.id;
}
@@ -87,6 +89,16 @@ namespace RH_Engine
return null;
}
public static string GetTerrainID(string json)
{
dynamic jsonData = JsonConvert.DeserializeObject(json);
if (jsonData.data.data.status == "ok")
{
return jsonData.data.data.data.uuid;
}
return null;
}
/// <summary>
/// method to get the uuid from requests for adding a node,route or road
/// </summary>
@@ -112,5 +124,19 @@ namespace RH_Engine
}
return null;
}
public static string GetChildUuid(string name, JArray children)
{
foreach (dynamic child in children)
{
if (child.name == name)
{
return child.uuid;
}
}
Console.WriteLine("Could not find id of " + name);
return null;
}
}
}

View File

@@ -4,6 +4,8 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Net.Sockets;
using System.Text;
@@ -17,13 +19,15 @@ namespace RH_Engine
//new PC("DESKTOP-M2CIH87", "Fabian"),
//new PC("T470S", "Shinichi"),
//new PC("DESKTOP-DHS478C", "semme"),
//new PC("HP-ZBOOK-SEM", "Sem"),
new PC("HP-ZBOOK-SEM", "Sem"),
//new PC("DESKTOP-TV73FKO", "Wouter"),
new PC("DESKTOP-SINMKT1", "Ralf van Aert"),
//new PC("DESKTOP-SINMKT1", "Ralf van Aert"),
//new PC("NA", "Bart")
};
private static ServerResponseReader serverResponseReader;
private static string terrainId = string.Empty;
private static string sessionId = string.Empty;
private static string tunnelId = string.Empty;
private static string cameraId = string.Empty;
@@ -32,6 +36,12 @@ namespace RH_Engine
private static string bikeId = string.Empty;
private static string headId = string.Empty;
private static double bikeSpeed = 6.66;
private static int bpm = 6;
private static int power = 6;
private static int resistance = 6;
private static string lastMessage = "No message received yet";
private static Dictionary<string, HandleSerial> serialResponses = new Dictionary<string, HandleSerial>();
private static void Main(string[] args)
@@ -81,7 +91,11 @@ namespace RH_Engine
//Console.WriteLine("GOT MESSAGE WITH SERIAL: " + message + "\n\n\n");
string serial = JSONParser.GetSerial(message);
//Console.WriteLine("Got serial " + serial);
if (serialResponses.ContainsKey(serial)) serialResponses[serial].Invoke(message);
if (serialResponses.ContainsKey(serial))
{
serialResponses[serial].Invoke(message);
//serialResponses.Remove(serial);
}
}
}
@@ -167,64 +181,69 @@ namespace RH_Engine
//Force(stream, mainCommand.DeleteNode(handRightId, "deleteHandR"), "deleteHandR", (message) => Console.WriteLine("Right hand deleted"));
});
//CreateTerrain(stream, mainCommand);
//Add route, bike and put camera and bike to follow route at same speed.
SendMessageAndOnResponse(stream, mainCommand.RouteCommand("routeID"), "routeID", (message) => routeId = JSONParser.GetResponseUuid(message));
SendMessageAndOnResponse(stream, mainCommand.AddBikeModel("bikeID"), "bikeID",
//SendMessageAndOnResponse(stream, mainCommand.RouteCommand("routeID"), "routeID", (message) => routeId = JSONParser.GetResponseUuid(message));
SendMessageAndOnResponse(stream, mainCommand.AddBikeModelAnim("bikeID",0.01f), "bikeID",
(message) =>
{
bikeId = JSONParser.GetResponseUuid(message);
Console.WriteLine("got bike id " + bikeId);
SendMessageAndOnResponse(stream, mainCommand.addPanel("panelAdd", bikeId), "panelAdd",
(message) =>
{
bool speedReplied = false;
bool moveReplied = true;
panelId = JSONParser.getPanelID(message);
WriteTextMessage(stream, mainCommand.ClearPanel(panelId));
SendMessageAndOnResponse(stream, mainCommand.MoveTo(panelId, "panelMove", new float[] { 0f, 0f, 0f }, "Z", 1, 5), "panelMove",
(message) =>
{
Console.WriteLine(message);
SendMessageAndOnResponse(stream, mainCommand.bikeSpeed(panelId, "bikeSpeed", 5.0), "bikeSpeed",
(message) =>
{
WriteTextMessage(stream, mainCommand.SwapPanel(panelId));
});
});
panelId = JSONParser.getPanelID(message);
Console.WriteLine("got panel id " + panelId);
showPanel(stream, mainCommand);
//while (!(speedReplied && moveReplied)) { }
while (cameraId == string.Empty) { }
SetFollowSpeed(5.0f, stream, mainCommand);
});
});
//Force(stream, mainCommand.addPanel("panelID", bikeId), "panelID",
// (message) =>
// {
// Console.WriteLine("panel response: " + message);
// panelId = JSONParser.GetResponseUuid(message);
// while(bikeId == string.Empty) { }
//while (cameraId == string.Empty) { }
//SetFollowSpeed(5.0f, stream, mainCommand);
// });
//SendMessageAndOnResponse(stream, maincommand.addpanel("panelid", bikeid), "panelid",
// (message) =>
// {
// console.writeline("panelid: " + message);
// //panelid = jsonparser.getpanelid(message);
// panelid = jsonparser.getresponseuuid(message);
// while (bikeid == string.empty) { }
// setfollowspeed(5.0f, stream, maincommand);
// });
//WriteTextMessage(stream, mainCommand.RoadCommand(routeId, "road"));
//WriteTextMessage(stream, mainCommand.ShowRoute("showRouteFalse", false));
});
});
//string groundplaneId = GetId("GroundPlane", stream, mainCommand);
//WriteTextMessage(stream, mainCommand.DeleteNode(groundplaneId, "none"));
//PlaceHouses(stream, mainCommand);
//WriteTextMessage(stream, mainCommand.TerrainCommand(new int[] { 256, 256 }, null));
//string command;
//WriteTextMessage(stream, mainCommand.SkyboxCommand(DateTime.Now.Hour));
Console.WriteLine("id of head " + GetId(Command.STANDARD_HEAD, stream, mainCommand));
}
private static void PlaceHouses(NetworkStream stream, Command mainCommand)
{
// public string AddModel(string nodeName, string serial, string fileLocation, float[] positionVector, float scalar, float[] rotationVector)
//string folderHouses = @"data\NetworkEngine\models\houses\set1\";
//SendMessageAndOnResponse(stream, mainCommand.AddModel("House1", "uselessSerial_atm", folderHouses + "house1.obj", new float[] { -20f, 1f, 0f }, 4 , new float[] { 0f, 0f, 0f }), "uselessSerial_atm", (message) => Console.WriteLine(message));
//WriteTextMessage(stream, mainCommand.AddModel("House1", "uselessSerial_atm", @"C:\Users\Ralf van Aert\Documents\AvansTI\NetworkEngine.18.10.10.1\NetworkEngine\data\NetworkEngine\models\houses\set1\house4.obj"));
PlaceHouse(stream, mainCommand, 2, new float[] { 10f, 1f, 30f }, 1);
PlaceHouse(stream, mainCommand, 1, new float[] { 42f, 1f, 22f }, new float[] { 0f, 90f, 0f }, 2);
PlaceHouse(stream, mainCommand, 11, new float[] { -20f, 1f, 0f }, new float[] { 0f, -35f, 0f }, 3);
PlaceHouse(stream, mainCommand, 7, new float[] { -15f, 1f, 50f }, new float[] { 0f, -50f, 0f }, 4);
PlaceHouse(stream, mainCommand, 24, new float[] { 40f, 1f, 40f }, new float[] { 0f, 75f, 0f }, 5);
PlaceHouse(stream, mainCommand, 22, new float[] { 34f, 1f, -20f }, 6);
PlaceHouse(stream, mainCommand, 14, new float[] { 0f, 1f, -20f }, new float[] { 0f, 210f, 0f }, 7);
}
private static void PlaceHouse(NetworkStream stream, Command mainCommand, int numberHousemodel, float[] position, int serialNumber)
{
PlaceHouse(stream, mainCommand, numberHousemodel, position, new float[] { 0f, 0f, 0f }, serialNumber);
}
private static void PlaceHouse(NetworkStream stream, Command mainCommand, int numberHousemodel, float[] position, float[] rotation, int serialNumber)
{
string folderHouses = @"data\NetworkEngine\models\houses\set1\";
SendMessageAndOnResponse(stream, mainCommand.AddModel("House1", "housePlacement" + serialNumber, folderHouses + "house" + numberHousemodel + ".obj", position, 4, rotation), "housePlacement" + serialNumber, (message) => Console.WriteLine(message));
}
/// <summary>
@@ -256,12 +275,32 @@ namespace RH_Engine
ImprovedPerlin improvedPerlin = new ImprovedPerlin(0, LibNoise.NoiseQuality.Best);
for (int i = 0; i < 256 * 256; i++)
{
height[i] = improvedPerlin.GetValue(x / 10, x / 10, x * 100) + 1;
height[i] = improvedPerlin.GetValue(x / 10, x / 10, x * 100)/3.5f + 1;
if (height[i] > 1.1f)
{
height[i] = height[i] * 0.8f;
}
else if (height[i] < 0.9f)
{
height[i] = height[i] * 1.2f;
}
x += 0.001f;
}
WriteTextMessage(stream, createGraphics.TerrainCommand(new int[] { 256, 256 }, height));
WriteTextMessage(stream, createGraphics.AddNodeCommand());
SendMessageAndOnResponse(stream, createGraphics.TerrainAdd(new int[] { 256, 256 }, height, "terrain"), "terrain",
(message) =>
{
SendMessageAndOnResponse(stream, createGraphics.renderTerrain("renderTerrain"), "renderTerrain",
(message) =>
{
terrainId = JSONParser.GetTerrainID(message);
string addLayerMsg = createGraphics.AddLayer(terrainId, "addLayer");
SendMessageAndOnResponse(stream, addLayerMsg, "addLayer", (message) => Console.WriteLine(""));
});
});
}
/// <summary>
@@ -303,11 +342,52 @@ namespace RH_Engine
return res;
}
private static void showPanel(NetworkStream stream, Command mainCommand)
{
//WriteTextMessage(stream, mainCommand.ColorPanel(panelId));
WriteTextMessage(stream, mainCommand.ClearPanel(panelId));
SendMessageAndOnResponse(stream, mainCommand.showBikespeed(panelId, "bikeSpeed", bikeSpeed), "bikeSpeed",
(message) =>
{
Console.WriteLine(message);
// TODO check if is drawn
});
SendMessageAndOnResponse(stream, mainCommand.showHeartrate(panelId, "bpm", bpm), "bpm",
(message) =>
{
Console.WriteLine(message);
// TODO check if is drawn
});
SendMessageAndOnResponse(stream, mainCommand.showPower(panelId, "power", power), "power",
(message) =>
{
Console.WriteLine(message);
// TODO check if is drawn
});
SendMessageAndOnResponse(stream, mainCommand.showResistance(panelId, "resistance", resistance), "resistance",
(message) =>
{
Console.WriteLine(message);
// TODO check if is drawn
});
SendMessageAndOnResponse(stream, mainCommand.showMessage(panelId, "message", lastMessage), "message",
(message) =>
{
Console.WriteLine(message);
// TODO check if is drawn
});
// Check if every text is drawn!
WriteTextMessage(stream, mainCommand.SwapPanel(panelId));
}
private static void SetFollowSpeed(float speed, NetworkStream stream, Command mainCommand)
{
WriteTextMessage(stream, mainCommand.RouteFollow(routeId, bikeId, speed, new float[] { 0, -(float)Math.PI / 2f, 0 }, new float[] { 0, 0, 0 }));
WriteTextMessage(stream, mainCommand.RouteFollow(routeId, cameraId, speed));
WriteTextMessage(stream, mainCommand.RouteFollow(routeId, panelId, speed, 0, "XYZ", 1, false, new float[] { 0, 0, 0 }, new float[] { 0f, 0f, 150f }));
//WriteTextMessage(stream, mainCommand.RouteFollow(routeId, panelId, speed, 1f, "XYZ", 1, false, new float[] { 0, 0, 0 }, new float[] { 0f, 5f, 5f }));
}
//string routeID, string nodeID, float speedValue, float offsetValue, string rotateValue, float smoothingValue, bool followHeightValue, float[] rotateOffsetVector, float[] positionOffsetVector)
private static void Force(NetworkStream stream, string message, string serial, HandleSerial action)

View File

@@ -9,6 +9,8 @@ namespace RH_Engine
public class ServerResponseReader
{
private bool running;
private Thread t;
public OnResponse callback
{
get; set;
@@ -23,7 +25,7 @@ namespace RH_Engine
public void StartRead()
{
Thread t = new Thread(() =>
t = new Thread(() =>
{
if (this.callback == null)
{
@@ -31,8 +33,9 @@ namespace RH_Engine
}
else
{
running = true;
Console.WriteLine("[SERVERRESPONSEREADER] Starting loop for reading");
while (true)
while (running)
{
string res = ReadPrefMessage(Stream);
//Console.WriteLine("[SERVERRESPONSEREADER] got message from server: " + res);
@@ -44,6 +47,13 @@ namespace RH_Engine
t.Start();
}
public void Stop()
{
running = false;
t.Join();
Stream.Close();
}
/// <summary>
/// reads a response from the server
/// </summary>

View File

@@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using Client;
using System.Timers;
using Newtonsoft.Json;
using System.Security.Cryptography;
using Util;
namespace Server
{
@@ -18,13 +19,12 @@ namespace Server
private byte[] totalBuffer = new byte[1024];
private int totalBufferReceived = 0;
private SaveData saveData;
private string username = null;
public string username = null;
private DateTime sessionStart;
private string fileName;
public string Username { get; set; }
private Timer timer;
private byte[] BikeDataBuffer;
private byte[] BPMDataBuffer;
public Client(Communication communication, TcpClient tcpClient)
{
@@ -33,14 +33,23 @@ namespace Server
this.tcpClient = tcpClient;
this.stream = this.tcpClient.GetStream();
this.fileName = Directory.GetCurrentDirectory() + "/userInfo.dat";
this.timer = new Timer();
this.BikeDataBuffer = new byte[16];
this.BPMDataBuffer = new byte[2];
this.timer.Interval = 1000;
this.timer.AutoReset = true;
this.timer.Elapsed += SendDataToDoctor;
stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(OnRead), null);
}
private void OnRead(IAsyncResult ar)
{
if (ar == null || (!ar.IsCompleted) || (!this.stream.CanRead) || !this.tcpClient.Client.Connected)
return;
int receivedBytes = this.stream.EndRead(ar);
if (totalBufferReceived + receivedBytes > 1024)
if (totalBufferReceived + receivedBytes > 1024)
{
throw new OutOfMemoryException("buffer too small");
@@ -66,6 +75,8 @@ namespace Server
}
}
if (ar == null || (!ar.IsCompleted) || (!this.stream.CanRead) || !this.tcpClient.Client.Connected)
return;
this.stream.BeginRead(this.buffer, 0, this.buffer.Length, new AsyncCallback(OnRead), null);
}
@@ -77,7 +88,7 @@ namespace Server
/// <summary>
/// TODO
/// handles all incoming data from the client
/// </summary>
/// <param name="message">including message length and messageId (can be changed)</param>
private void HandleData(byte[] message)
@@ -93,43 +104,43 @@ namespace Server
string identifier;
bool isJson = DataParser.getJsonIdentifier(message, out identifier);
if (isJson)
{
switch (identifier)
{
case DataParser.LOGIN:
string username;
string password;
bool worked = DataParser.GetUsernamePassword(payloadbytes, out username, out password);
if (worked)
if (handleLogin(payloadbytes))
communication.NewLogin(this);
break;
case DataParser.LOGIN_DOCTOR:
if (communication.Doctor != null)
return;
if (handleLogin(payloadbytes))
{
if (verifyLogin(username, password))
{
Console.WriteLine("Log in");
this.username = username;
sendMessage(DataParser.getLoginResponse("OK"));
sendMessage(DataParser.getStartSessionJson());
}
else
{
sendMessage(DataParser.getLoginResponse("wrong username or password"));
}
}
else
{
sendMessage(DataParser.getLoginResponse("invalid json"));
communication.Doctor = this;
}
break;
case DataParser.START_SESSION:
this.saveData = new SaveData(Directory.GetCurrentDirectory() + "/" + this.username + "/" + sessionStart.ToString("yyyy-MM-dd HH-mm-ss"));
this.communication.StartSessionUser(DataParser.getUsernameFromJson(payloadbytes));
this.timer.Start();
break;
case DataParser.STOP_SESSION:
this.saveData = null;
this.communication.StopSessionUser(DataParser.getUsernameFromJson(payloadbytes));
this.timer.Stop();
break;
case DataParser.SET_RESISTANCE:
worked = DataParser.getResistanceFromResponseJson(payloadbytes);
Console.WriteLine($"set resistance worked is " + worked);
//bool worked = DataParser.getResistanceFromResponseJson(payloadbytes);
communication.SendMessageToClient(DataParser.getUsernameFromJson(payloadbytes), message);
//set resistance on doctor GUI
break;
case DataParser.DISCONNECT:
communication.LogOff(this);
break;
case DataParser.MESSAGE:
communication.SendMessageToClient(DataParser.getUsernameFromJson(payloadbytes), message);
break;
default:
Console.WriteLine($"Received json with identifier {identifier}:\n{Encoding.ASCII.GetString(payloadbytes)}");
@@ -141,24 +152,44 @@ namespace Server
dynamic json = JsonConvert.DeserializeObject(Encoding.ASCII.GetString(payloadbytes));
}
else if (DataParser.isRawData(message))
{
Console.WriteLine(BitConverter.ToString(payloadbytes));
if (payloadbytes.Length == 8)
else if (DataParser.isRawDataBikeServer(message))
{
saveData?.WriteDataRAWBike(payloadbytes);
Array.Copy(this.BikeDataBuffer, 0, this.BikeDataBuffer, 8, 8);
Array.Copy(payloadbytes, 0, this.BikeDataBuffer, 0, 8);
}
else if (payloadbytes.Length == 2)
else if (DataParser.isRawDataBPMServer(message))
{
saveData?.WriteDataRAWBPM(payloadbytes);
Array.Copy(payloadbytes, 0, this.BikeDataBuffer, 0, 2);
}
}
private bool handleLogin(byte[] payloadbytes)
{
string username;
string password;
bool worked = DataParser.GetUsernamePassword(payloadbytes, out username, out password);
if (worked)
{
if (verifyLogin(username, password))
{
Console.WriteLine("Log in");
this.username = username;
sendMessage(DataParser.getLoginResponse("OK"));
return true;
}
else
{
Console.WriteLine("received raw data with weird lenght " + BitConverter.ToString(payloadbytes));
sendMessage(DataParser.getLoginResponse("wrong username or password"));
}
}
else
{
sendMessage(DataParser.getLoginResponse("invalid json"));
}
return false;
}
public void sendMessage(byte[] message)
@@ -168,7 +199,7 @@ namespace Server
private bool verifyLogin(string username, string password)
{
Console.WriteLine("got hashes " + username + "\n" + password);
Console.WriteLine($"Got username {username} and password {password}");
if (!File.Exists(fileName))
@@ -183,27 +214,23 @@ namespace Server
{
Console.WriteLine("file exists, located at " + Path.GetFullPath(fileName));
string[] usernamesPasswords = File.ReadAllLines(fileName);
if (usernamesPasswords.Length == 0)
{
newUsers(username, password);
return true;
}
foreach (string s in usernamesPasswords)
{
string[] combo = s.Split(" ");
if (combo[0] == username)
{
Console.WriteLine("correct info");
Console.WriteLine("username found in file");
return combo[1] == password;
}
}
Console.WriteLine("combo was not found in file");
Console.WriteLine("username not found in file");
newUsers(username, password);
return true;
}
Console.WriteLine("false");
return false;
}
@@ -217,10 +244,6 @@ namespace Server
}
}
public static string ByteArrayToString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
@@ -228,5 +251,22 @@ namespace Server
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
public void StartSession()
{
this.saveData = new SaveData(Directory.GetCurrentDirectory() + "/" + this.username + "/" + sessionStart.ToString("yyyy-MM-dd HH-mm-ss"));
}
public void StopSession()
{
this.saveData = null;
}
private void SendDataToDoctor(object sender, ElapsedEventArgs e)
{
this.communication.Doctor?.sendMessage(DataParser.GetRawBikeDataDoctor(this.BikeDataBuffer.Take(8).ToArray(), this.username));
this.communication.Doctor?.sendMessage(DataParser.GetRawBikeDataDoctor(this.BikeDataBuffer.Skip(8).ToArray(), this.username));
this.communication.Doctor?.sendMessage(DataParser.GetRawBikeDataDoctor(this.BikeDataBuffer, this.username));
}
}
}

View File

@@ -1,10 +1,7 @@
using Client;
using System;
using System;
using System.Collections.Generic;
using System.IO.Pipes;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using Util;
namespace Server
{
@@ -12,8 +9,24 @@ namespace Server
{
private TcpListener listener;
private List<Client> clients;
private Client doctor;
private Client mDoctor;
public Client Doctor
{
get
{
return this.mDoctor;
}
set
{
this.mDoctor = value;
if (this.mDoctor != null)
this.clients.ForEach((client) =>
{
this.mDoctor.sendMessage(DataParser.getNewConnectionJson(client.username));
client.sendMessage(DataParser.getNewConnectionJson(this.mDoctor.username));
});
}
}
public Communication(TcpListener listener)
{
this.listener = listener;
@@ -34,22 +47,68 @@ namespace Server
var tcpClient = listener.EndAcceptTcpClient(ar);
Console.WriteLine($"Client connected from {tcpClient.Client.RemoteEndPoint}");
clients.Add(new Client(this, tcpClient));
if (doctor == null)
{
doctor = clients.ElementAt(0);
}
else
{
doctor.sendMessage(DataParser.getLoginResponse("new client"));
}
new Client(this, tcpClient);
listener.BeginAcceptTcpClient(new AsyncCallback(OnConnect), null);
}
internal void Disconnect(Client client)
public void NewLogin(Client client)
{
clients.Remove(client);
this.clients.Add(client);
if (this.Doctor != null)
{
Doctor.sendMessage(DataParser.getNewConnectionJson(client.username));
client.sendMessage(DataParser.getNewConnectionJson(Doctor.username));
}
}
public void LogOff(Client client)
{
if (this.Doctor == client)
{
this.clients.ForEach((client) =>
{
client.sendMessage(DataParser.getDisconnectJson(this.mDoctor.username));
});
this.Doctor = null;
}
Doctor?.sendMessage(DataParser.getDisconnectJson(client.username));
this.clients.Remove(client);
}
public void StartSessionUser(string user)
{
foreach (Client client in clients)
{
if (client.username == user)
{
client.sendMessage(DataParser.getStartSessionJson(user));
client.StartSession();
}
}
}
public void StopSessionUser(string user)
{
foreach (Client client in clients)
{
if (client.username == user)
{
client.sendMessage(DataParser.getStopSessionJson(user));
client.StopSession();
}
}
}
public void SendMessageToClient(string user, byte[] message)
{
foreach (Client c in clients)
{
if (c.username == user)
{
c.sendMessage(message);
}
}
}
}
}

View File

@@ -63,12 +63,20 @@ namespace Server
{
// do nothing
}
using (BinaryWriter sw = new BinaryWriter(File.Open(fileLocation, FileMode.Create)))
using (var fileStream = new FileStream(fileLocation, FileMode.Append, FileAccess.Write, FileShare.None))
using (var bw = new BinaryWriter(fileStream))
{
sw.Seek(length, SeekOrigin.End);
sw.Write(data);
sw.Flush();
//sw.BaseStream.Seek(length, SeekOrigin.Begin);
bw.Write(data);
bw.Flush();
//Console.WriteLine("wrote at " + bw.BaseStream.Position);
}
//using (BinaryReader binaryReader = new BinaryReader(File.Open(fileLocation, FileMode.Open)))
//{
// byte[] totalArray = new byte[binaryReader.BaseStream.Length];
// binaryReader.BaseStream.Read(totalArray, 0, (int)binaryReader.BaseStream.Length);
// Console.WriteLine("all data is " + BitConverter.ToString(totalArray));
//}
}
/// <summary>
@@ -86,6 +94,7 @@ namespace Server
{
FileInfo fi = new FileInfo(this.path + rawBPMFilename);
int length = (int)fi.Length;
//Console.WriteLine("length " + length);
byte[] output = new byte[outputSize];
@@ -93,15 +102,20 @@ namespace Server
int readSize = messageSize * averageOver;
byte[] readBuffer = new byte[readSize];
using (FileStream fileStream = new FileStream(this.path + rawBPMFilename, FileMode.Open, FileAccess.Read))
using (BinaryReader binaryReader = new BinaryReader(File.Open(this.path + rawBPMFilename, FileMode.Open)))
{
for (int i = 1; i >= outputSize; i++)
//byte[] totalArray = new byte[binaryReader.BaseStream.Length];
//binaryReader.BaseStream.Read(totalArray, 0, (int)binaryReader.BaseStream.Length);
//Console.WriteLine("all data is " + BitConverter.ToString(totalArray));
for (int i = 1; i <= outputSize; i++)
{
if (length - (i * readSize) < 0)
{
break;
}
fileStream.Read(readBuffer, length - (i * readSize), readSize);
binaryReader.BaseStream.Seek(length - (i * readSize), SeekOrigin.Begin);
binaryReader.BaseStream.Read(readBuffer, 0, readSize);
//Console.WriteLine("read " + binaryReader.BaseStream.Position + " and size " + readSize + " with value " + BitConverter.ToString(readBuffer));
//handling data
int total = 0;

View File

@@ -10,7 +10,8 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Client\Client.csproj" />
<ProjectReference Include="..\ClientApp\ClientApp.csproj" />
<ProjectReference Include="..\DoctorApp\DoctorApp.csproj" />
</ItemGroup>
<Import Project="..\Hashing\Hashing.projitems" Label="Shared" />