-
Notifications
You must be signed in to change notification settings - Fork 2
/
get-started.html
1106 lines (1015 loc) · 59.5 KB
/
get-started.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
---
layout: page
title: Get started
permalink: /get-started/
icon: fa-rocket
classes: get-started col-md-10 col-md-offset-1 col-sm-10 col-sm-offset-1
stylesheet: /css/pages/get-started.css
---
<div class="introduction">
<div class="row">
<div class="col-xs-12">
<h1>We're glad you chose Woopsa!</h1>
<p>Really! We think that once you've learned how to use Woopsa, you won't ever look back. But because our mission is to get Woopsa on as many devices as possible, we need you to tell us what you want to do.</p>
</div>
</div>
<div class="row server-chooser">
<h3>I want to make a server in:</h3>
<div class="col-sm-4 col-xs-4">
<a href="#csharp-server" class="btn btn-lg btn-primary">C# / .NET</a>
</div>
<div class="col-sm-4 col-xs-4">
<a href="#node-server" class="btn btn-lg btn-primary">Node.js</a>
</div>
<div class="col-sm-4 col-xs-4">
<a href="#embedded-server" class="btn btn-lg btn-primary">Embedded C</a>
</div>
</div>
<h3 class="or">- or -</h3>
<div class="row client-chooser">
<h3>I want to make a client in:</h3>
<div class="col-sm-3 col-sm-offset-3 col-xs-6">
<a href="#javascript-client" class="btn btn-lg btn-primary">JavaScript</a>
</div>
<div class="col-sm-3 col-xs-6">
<a href="#csharp-client" class="btn btn-lg btn-primary">C# / .NET</a>
</div>
</div>
</div>
<div id="table-of-contents" class="part-block">
<h1>Table of contents</h1>
<ul>
<li>
<a href="#csharp-server">Using the C# / .NET server</a>
<ul>
<li><a href="#csharp-server-introduction">Introduction</a></li>
<li><a href="#csharp-server-getting-the-assembly-and-using-it">Getting the assembly and using it</a></li>
<li><a href="#csharp-server-creating-your-first-server">Creating your first server</a></li>
<li>
<a href="#csharp-server-going-deeper-with-the-server">Going deeper with the server</a>
<ul>
<li><a href="#csharp-server-publishing-only-part-of-your-model">Publishing only part of your model</a></li>
<li><a href="#csharp-server-working-with-a-complete-hierarchy">Working with a complete hierarchy</a></li>
<li><a href="#csharp-server-using-ssl-tls">Using SSL/TLS</a></li>
<li><a href="#csharp-server-using-authentication">Using authentication</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#node-server">Using the Node.js server</a>
<ul>
<li><a href="#node-server-introduction">Introduction</a></li>
<li><a href="#node-server-getting-the-package">Getting the package</a></li>
<li><a href="#node-server-creating-your-first-server">Creating your first server</a></li>
<li>
<a href="#node-server-going-deeper-with-the-server">Going deeper with the server</a>
<ul>
<li><a href="#node-server-force-a-different-type-with-the-typer-function">Force a different type with the typer function</a></li>
<li><a href="#node-server-using-asynchronous-functions-in-your-model">Using asynchronous functions in your model</a></li>
<li><a href="#node-server-integrating-woopsa-into-your-existing-express-app">Integrating Woopsa into your existing Express app</a></li>
<li><a href="#node-server-using-ssl-tls">Using SSL/TLS</a></li>
<li><a href="#node-server-using-authentication">Using authentication</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#embedded-server">Using the Embedded C server</a>
<ul>
<li><a href="#embedded-server-introduction">Introduction</a></li>
<li><a href="#embedded-server-getting-the-code">Getting the code</a></li>
<li><a href="#embedded-server-how-to-use-woopsa-embedded">How to use Woospa Embedded</a></li>
<li>
<a href="#embedded-server-creating-your-first-server">Creating your first server</a>
<ul>
<li><a href="#embedded-server-arduino-ethernet-shield-2-example">Arduino Ethernet Shield 2 example</a></li>
<li><a href="#embedded-server-standard-sockets-example">Standard sockets example</a></li>
</ul>
</li>
<li><a href="#embedded-server-serving-other-files">Serving other content</a></li>
<li><a href="#embedded-server-limitations">Limitations</a></li>
</ul>
</li>
<li>
<a href="#javascript-client">Using the JavaScript client</a>
<ul>
<li><a href="#javascript-client-introduction">Introduction</a></li>
<li><a href="#javascript-client-getting-the-code">Getting the code</a></li>
<li><a href="#javascript-client-creating-your-first-client">Creating your first client</a></li>
<li>
<a href="#javascript-client-basic-usage">Basic usage</a>
<ul>
<li><a href="#javascript-client-exploring-the-object-tree-with-meta">Exploring the object tree with <samp>meta</samp></a></li>
<li><a href="#javascript-client-reading-a-property-with-read">Reading a property with <samp>read</samp></a></li>
<li><a href="#javascript-client-writing-to-a-property-with-write">Writing to a property with <samp>write</samp></a></li>
<li><a href="#javascript-client-calling-a-method-function-with-invoke">Calling a method/function with <samp>invoke</samp></a></li>
</ul>
</li>
<li><a href="#javascript-client-listening-for-value-changes">Listening for value changes</a></li>
<li><a href="#javascript-client-using-ssl-tls">Using SSL/TLS</a></li>
<li><a href="#javascript-client-using-authentication">Using authentication</a></li>
</ul>
</li>
<li>
<a href="#csharp-client">Using the C# / .NET client</a>
<ul>
<li><a href="#csharp-client-introduction">Introduction</a></li>
<li><a href="#csharp-client-getting-the-assembly-and-using-it">Getting the assembly and using it</a></li>
<li><a href="#csharp-client-creating-your-first-client">Creating your first client</a></li>
<li>
<a href="#csharp-client-basic-usage">Basic usage</a>
<ul>
<li><a href="#csharp-client-exploring-the-object-tree">Exploring the object tree</a></li>
<li><a href="#csharp-client-reading-a-property">Reading a property</a></li>
<li><a href="#csharp-client-writing-to-a-property">Writing to a property</a></li>
<li><a href="#csharp-client-calling-a-method-function">Calling a method/function</a></li>
</ul>
</li>
<li><a href="#csharp-client-listening-for-value-changes">Listening for value changes</a></li>
<li><a href="#csharp-client-bound-api">Bound API</a></li>
<li><a href="#csharp-client-unbound-api">Unbound API</a></li>
<li><a href="#csharp-client-even-simpler-using-the-dynamic-client">Even simpler: using the dynamic client</a></li>
<li><a href="#csharp-client-using-ssl-tls">Using SSL/TLS</a></li>
<li><a href="#csharp-client-using-authentication">Using authentication</a></li>
</ul>
</li>
</ul>
</div>
<div id="csharp-server" class="part-block">
<h1>Using the C# / .NET server</h1>
<h2 id="csharp-server-introduction">Introduction</h2>
<p>Making a Woopsa server is extremely simple and requires just <strong>one</strong> line of code if you're using our reflector! Just give the Woopsa server a class and it will do the rest, automagically!</p>
<p>The C# / .NET server is included in the <samp>Woopsa</samp> assembly. We created the Woopsa library with Mono in mind, trying to use only the most standard .NET components for maximum compatibility.</p>
<h2 id="csharp-server-getting-the-assembly-and-using-it">Getting the assembly and using it</h2>
<p>Start off by <a href="/download/">downloading</a> Woopsa and get the Woopsa.dll assembly inside the DotNet folder. Add this assembly as a reference in your Visual Studio project and add the following line to the top of your source file:</p>
<div class="code">
{% highlight csharp %}
using Woopsa;
{% endhighlight %}
</div>
<p>That's it! You're now ready to create your server.</p>
<h2 id="csharp-server-creating-your-first-server">Creating your first server</h2>
<p class="note"><strong>Note:</strong> By default, the Woopsa server resides on <strong>port 80</strong> and has a base path of <samp>/woopsa</samp>, meaning your first server will be available from <samp>http://localhost/woopsa</samp></p>
<p>To create a simple server, you first need some data to publish to the world. You can set your Woopsa server to handle any C# object, but for example's sake, this is the class we are going to use:</p>
<div class="code">
{% highlight csharp %}
public class WeatherStation
{
public double Temperature { get; private set; }
public double Sensitivity { get; set; }
public string GetWeatherAtDate(DateTime date)
{
switch (date.DayOfWeek)
{
case DayOfWeek.Monday:
return "cloudy";
default:
return "sunny";
}
}
}
{% endhighlight %}
</div>
<p>Once you've written your class, you can simply create the new Woopsa server and give it your root object:</p>
<div class="code">
{% highlight csharp %}
WeatherStation station = new WeatherStation();
WoopsaServer server = new WoopsaServer(station);
{% endhighlight %}
</div>
<p>That's it! Your Woopsa server is now available on <samp>http://localhost/woopsa</samp>. The server starts in its own thread and uses a thread pool to serve multiple clients at once. This means you really don't have to worry about anything ... just work with your <samp>WeatherStation</samp> like you would with any other object, and Woopsa takes care of everything for you. Including publish/subscribe events!</p>
<p class="note"><strong>Note:</strong> Woopsa is completely web-based. This means you can see your data simply with a browser! Go on <samp>http://localhost/woopsa/meta/</samp> too see the metadata of your root object, or <samp>http://localhost/woopsa/read/Temperature</samp> to read a piece of data!<br />
<a href="{{ '/img/screenshot-meta.png' | prepend:site.baseurl }}"><img src="{{ '/img/screenshot-meta.png' | prepend:site.baseurl }}" class="img-responsive-half"/></a>
<a href="{{ '/img/screenshot-read-temperature.png' | prepend:site.baseurl }}"><img src="{{ '/img/screenshot-read-temperature.png' | prepend:site.baseurl }}" class="img-responsive-half"/></a>
</p>
<h2 id="csharp-server-going-deeper-with-the-server">Going deeper with the server</h2>
<h3 id="csharp-server-publishing-only-part-of-your-model">Publishing only part of your model</h3>
<p>If, for any reason, you don't want to publish your entire object and want to choose exactly what to publish, you can! There are two metadata attributes you can use in C# to accomplish this.</p>
<ul>
<li><samp>[WoopsaVisible(false)]</samp> Applies to properties, fields and methods. This tells the reflector to not publish this item.</li>
<li><samp>[WoopsaVisibility(WoopsaVisibility.None)]</samp> Applies to classes. Just put this attribute on your class. All content on your instances will not be published </li>
</ul>
<p>In summary, this is what your new class should look like:</p>
<div class="code">
{% highlight csharp %}
[WoopsaVisibility(WoopsaVisibility.None)]
public class WeatherStation
{
[WoopsaVisible(false)]
public double Temperature { get; private set; }
[WoopsaVisible(true)]
public double Sensitivity { get; set; }
[WoopsaVisible(false)]
public string GetWeatherAtDate(DateTime date)
{
switch (date.DayOfWeek)
{
case DayOfWeek.Monday:
return "cloudy";
default:
return "sunny";
}
}
}
{% endhighlight %}
</div>
<p class="note"><strong>Note:</strong> By default, the entire model is published </p>
<h3 id="csharp-server-working-with-a-complete-hierarchy">Working with a complete hierarchy</h3>
<p>The reflector works very well, even with nested objects. The object tree is explored on creation and if you change the embedded objects in your classes, the new data would be right available.</p>
<p>Here's an example of using embedded objects:</p>
<div class="code">
{% highlight csharp %}
public class SmokeDetector
{
public bool SmokeDetected { get; private set; }
}
public class VentilationSystem
{
public VentilationSystem()
{
SmokeDetector = new SmokeDetector();
}
public bool Power { get; private set; }
public SmokeDetector SmokeDetector { get; set; }
}
...
VentilationSystem system = new VentilationSystem();
WoopsaServer server = new WoopsaServer(system);
...
system.SmokeDetector = new SmokeDetector(); // This would update the server cache!!
{% endhighlight %}
</div>
<p>That's it! If you want to know more about how to use the server, please take a look at some of the examples available in the source code. You can also read the <a href="/specifications/">full specifications</a> of the Woopsa protocol to get a better understanding of how everything works!</p>
<h3 id="csharp-server-using-ssl-tls">Using SSL/TLS</h3>
<p>Because Woopsa is based on HTTP, adding SSL/TLS is a breeze! You just need to specify port 443, add a certificate and you're good-to-go. The internal <samp>WebServer</samp> (Woopsa's own lightweight HTTP server) has a concept called <em>Pre-route Processing</em> which allows it to manipulate the data stream before the actual HTTP route handling is done. This makes adding SSL/TLS as easy as adding a <samp>TlsProcessor</samp> to the <samp>WebServer</samp>'s <samp>PreRouteProcessors</samp>:
{% highlight csharp %}
WeatherStation station = new WeatherStation();
WoopsaServer server = new WoopsaServer(station, 443);
server.WebServer.PreRouteProcessors.Add(new TlsProcessor("{path-to-your-certificate.p12}","{private-key-password}"));
{% endhighlight %}
<p class="note"><strong>Note:</strong> Internally, this processor uses .NET's <samp>SslStream</samp> and <samp>X509Certificate2</samp> coupled with <samp>AuthenticateAsServer()</samp>. This means that the certificate you pass to the processor must be a PKCS12 package with a password-protected private key embedded in it. Creating certificates is not related to Woopsa and is not covered in this guide.</p>
<h3 id="csharp-server-using-authentication">Using authentication</h3>
<p>Because Woopsa is based on HTTP, authentication is done with the simple WWW-Authenticate mechanism. This means that clients wishing to connect to your authentication-enabled Woopsa server will need to provide the <samp>Authorization</samp> header with proper Base64-encoded credentials.</p>
<p>To enable authentication on the server, you simply need to give the server a <samp>SimpleAuthenticator</samp> through the <samp>Authenticator</samp> property. You can then check against the username and password as you wish, and return <samp>true</samp> on success or <samp>false</samp> on failure.</p>
<p>To disable authentication on the server, simply set <samp>Authenticator</samp> to <samp>null</samp> (which is the case by default).</p>
<div class="code">
{% highlight csharp %}
private void authenticate(object sender, AuthenticationCheckEventArgs e)
{
e.IsAuthenticated = e.Username == "admin" && e.Password == "1";
}
...
WeatherStation root = new WeatherStation();
WoopsaServer woopsaServer = new WoopsaServer(root, 80);
woopsaServer.Authenticator = new SimpleAuthenticator("Woopsa server", authenticate);
{% endhighlight %}
</div>
<p>Of course, SSL/TLS and authentication work together seamlessly and thus allow full server and client-side authentication. That's the magic of using standards!</p>
</div>
<div id="node-server" class="part-block">
<h1>Using the Node.js server</h1>
<h2 id="node-server-introduction">Introduction</h2>
<p>Making a Woopsa server in Node.js is extremely simple and requires just <strong>one</strong> line of code if you're using the default options. The Woopsa server uses <a href="http://expressjs.com">express</a> in the background, which means it can integrate with your already-existing app if you so desire!</p>
<p>The Node.js server is included in the <samp>woopsa</samp> npm package. Woopsa for Node.js embraces the asynchronous nature of JavaScript, allowing you to plug it into any type of application and very quickly create a RESTful interface to your application.</p>
<h2 id="node-server-getting-the-package">Getting the package</h2>
<p>Because the Woopsa Node.js server is an npm package, installing the library is as simple as:</p>
<div class="code command">
<div class="highlight">
<pre><code class="language-cli" data-lang="cli">npm install woopsa</code></pre>
</div>
</div>
<p>Of course, you can also add it as a dependency to your <samp>package.json</samp> file, but this isn't covered in this tutorial. Once the package has been added to your project, you can simply require it using requirejs:</p>
<div class="code">
{% highlight javascript %}
var woopsa = require('woopsa');
{% endhighlight %}
</div>
<p>And that's it! You are now ready to use the Woopsa Node.js library!</p>
<h2 id="node-server-creating-your-first-server">Creating your first server</h2>
<p class="note"><strong>Note:</strong> By default, the Woopsa server resides on <strong>port 80</strong> and has a base path of <samp>/woopsa</samp>, meaning your first server will be available from <samp>http://localhost/woopsa</samp></p>
<p>To create a simple server, you first need some data to publish to the world. So we'll just construct a JavaScript object using the standard JSON syntax:</p>
<div class="code">
{% highlight javascript %}
var weatherStation = {
Temperature: 24.2,
IsRaining: false,
Sensitivity: 0.5,
Altitude: 430,
City: "Geneva",
Time: new Date(),
GetWeatherAtDate: function (date){
var date = new Date(date);
if ( date.getDay() === 1 )
return "rainy";
else
return "sunny";
},
Thermostat: {
SetPoint: 24.0
}
}
{% endhighlight %}
</div>
<p>Once you've written your data, you can simply create the new Woopsa server and give it your root object:</p>
<div class="code">
{% highlight javascript %}
var woopsaServer = new woopsa.Server(weatherStation);
{% endhighlight %}
</div>
<p>That's it! Your Woopsa server is now available on <samp>http://localhost/woopsa</samp>. The server creates a new express app and uses its inner routing mechanism to provide the Woopsa functionality. This means you really don't have to worry about anything ... just work with your <samp>weatherStation</samp> like you would with any other object, and Woopsa takes care of everything for you. Including publish/subscribe events (by polling the value of your object).</p>
<p class="note"><strong>Note:</strong> Woopsa is completely web-based. This means you can see your data simply with a browser! Go on <samp>http://localhost/woopsa/meta/</samp> too see the metadata of your root object, or <samp>http://localhost/woopsa/read/Temperature</samp> to read a piece of data!<br />
<a href="{{ '/img/screenshot-meta-node.png' | prepend:site.baseurl }}"><img src="{{ '/img/screenshot-meta-node.png' | prepend:site.baseurl }}" class="img-responsive-half"/></a>
<a href="{{ '/img/screenshot-read-temperature.png' | prepend:site.baseurl }}"><img src="{{ '/img/screenshot-read-temperature.png' | prepend:site.baseurl }}" class="img-responsive-half"/></a>
</p>
<h2 id="node-server-going-deeper-with-the-server">Going deeper with the server</h2>
<h3 id="node-server-force-a-different-type-with-the-typer-function">Force a different type with the typer function</h3>
<p>You will have noticed in our example <samp>GetWeatherAtDate</samp> function, we first have to parse the date (as illustrated by <samp>var date = new Date(date)</samp>), because it's given to us as a string. This is because JavaScript is a completely dynamic language, and as such we cannot know in advance what type of argument our function needs. Thus, when you are using the reflector, all function arguments and return types are set to "Text". This is also the case for properties whose values evaluate to a string.</p>
<p>This is where the <em>Typer</em> function comes into play. When you create your Woopsa server, you can pass it a function that will be called <em>every time</em> the type needs to be known. This function is called a <em>Typer</em> function and is called with two arguments: the <em>path</em> of the Woopsa element we need a type for, and the <em>inferred</em> type. The inferred type is what the Woopsa library <em>*thinks*</em> is the correct type for this element. Your <em>Typer</em> function then simply needs to return a Woopsa type. You can either return the inferred type, or the Woopsa type of your choice based on the path.</p>
<p>To set it up, simply set the <samp>typer</samp> property in the options to a function that accepts two parameters. For example, if we want to do this for the <samp>date</samp> argument of our <samp>GetWeatherAtDate</samp> function, here's how we would do it:</p>
<div class="code">
{% highlight javascript %}
var woopsaServer = new woopsa.Server(weatherStation, {
typer: function (path, inferredType){
if ( path === "/GetWeatherAtDate/date" )
return "DateTime";
else
return inferredType;
}
});
{% endhighlight %}
</div>
<p>Now that we've done this, Woopsa will automatically parse values as a JavaScript <samp>Date</samp> object before passing it to our function. Thus, we can change the declaration of our <samp>GetWeatherAtDate</samp> function and make it a little bit shorter:</p>
<div class="code">
{% highlight javascript %}
var weatherStation = {
...
GetWeatherAtDate: function (date){
if ( date.getDay() === 1 )
return "rainy";
else
return "sunny";
}
...
}
{% endhighlight %}
</div>
<p>This also has the advantage of letting the Woopsa server do some type sanity checks and throw the appropriate errors in case the client tries doing something nonsensical with types.</p>
<h3 id="node-server-using-asynchronous-functions-in-your-model">Using asynchronous functions in your model</h3>
<p>JavaScript, especially when used in Node.js applications, is asynchronous in nature. So what do you do when you need to read/write a file or write to a socket in a function? And how do you send that delayed result through Woopsa?</p>
<p>Under the hood, the Woopsa server is fully asynchronous for <samp>read</samp>, <samp>write</samp> and <samp>invoke</samp> operations. The reflector just hides that complexity from you. When it detects a function in your object, it creates a <samp>WoopsaMethod</samp> calling your function. The reflector, however, was made to be extended with additional manual functions.</p>
<p>The WoopsaServer object publishes an object called <samp>element</samp> to which you can add additional functions. To do this, simply create a new <samp>WoopsaMethodAsync</samp> object, passing it a function that accepts a callback function as last parameter. When you have your result, you can simply call the callback function with your return value. For example, we can make a function that returns "Hello world" after a certain amount of time.</p>
<div class="code">
{% highlight javascript %}
...
var helloAfterNSeconds = new woopsa.Types.WoopsaMethodAsync(
"HelloAfterNSeconds", // name of the method
"Text", // return type of your method
function (waitTime, done){ // The actual function that will be called upon invoke
setTimeout(function (){
done("Hello world"); // calling done "closes the loop" and allows the Woopsa server to respond
}, waitTime * 1000);
},
[{"waitTime": "Integer"}] // an array of key-value pairs for the types
);
woopsaServer.element.addMethod(helloAfterNSeconds);
{% endhighlight %}
</div>
<p>The Woopsa server will then publish a method called <em>HelloAfterNSeconds</em> that is fully asynchronous. This has the advantage of making your server 100% non-blocking, just like JavaScript was meant to be!</p>
<p class="note"><strong>Note:</strong> Your function does not need to return a value. If you throw an exception asynchronously, it will crash your server. In the case of an asynchronous error, you should instead call the <samp>done</samp> callback that way:<br> <samp>done(null, new woopsa.Exceptions.WoopsaException("Your error goes here."));</samp><br>The first argument doesn't necessarly have to be <samp>null</samp>, but it will be ignored either way, as the server will return your error to the client instead of the value.</p>
<h3 id="node-server-integrating-woopsa-into-your-existing-express-app">Integrating Woopsa into your existing Express app</h3>
<p>The Woopsa server uses express under the hood. If you already have a web server running in your application and want the Woopsa server to reside on the same port, you can! Just specify an <samp>expressApp</samp> property in the <samp>WoopsaServer</samp> constructor. The Woopsa server will then just add its own routes alongside your own, making sure both your webapp and Woopsa can live side-by-side.</p>
<div class="code">
{% highlight javascript %}
var app = express(); // Or however your own app is initialized/called
...
// Your app's code goes here
...
var woopsaServer = new woopsa.Server(weatherStation, {
pathPrefix: '/woopsa/', // The Woopsa server will be reacheable on /woopsa/. This is the default
expressApp: app // When expressApp is not null, Woopsa and express will live side-by-side
});
{% endhighlight %}
</div>
<p class="note"><strong>Note:</strong> You'll have to be careful in your app not to add middleware that interferes with Woopsa. For example, setting a global middleware function that adds some headers might break Woopsa functionality in some situations. The Woopsa server will never pollute your own middleware chain, as everything is prefixed.</p>
<h3 id="node-server-using-ssl-tls">Using SSL/TLS</h3>
<p>Because Woopsa uses express and Node.js's http module under the hood, using SSL/TLS with express is out of the scope of this guide. We recommend following <a href="http://blog.mgechev.com/2014/02/19/create-https-tls-ssl-application-with-express-nodejs/">this tutorial</a> which covers the basics of serving your content through https with express.</p>
<p>Once you have an express server listening on https, you can simply read the section above to learn how to pass an already existing express app to the Woopsa server.</p>
<h3 id="node-server-using-authentication">Using authentication</h3>
<p>Because Woopsa is based on HTTP, authentication is done with the simple <samp>WWW-Authenticate</samp> mechanism. This means that clients wishing to connect to your authentication-enabled Woopsa server will need to provide the Authorization header. This is already handled for you natively in all Woospa clients.</p>
<p>To enable authentication on the server, you simply need to set the <samp>checkAuthenticate</samp> option in the Woopsa server constructor to a <samp>function (username, password)</samp> which the Woopsa server will call upon every request. You can then check against this username and password as you wish, and return <samp>true</samp> on success or <samp>false</samp> on failure.</p>
<div class="code">
{% highlight javascript %}
var woopsaServer = new WoopsaServer(weatherStation, {
checkAuthenticate: function (username, password){
if ( username === 'admin' && password === 'secret' ){
return true;
}else{
return false;
}
}
});
{% endhighlight %}
</div>
<p>Of course, SSL/TLS and authentication work together seamlessly and thus allow full server and client-side authentication. That's the magic of using standards!</p>
</div>
<div id="embedded-server" class="part-block">
<h1>Using the Embedded server</h1>
<h2 id="embedded-server-introduction">Introduction</h2>
<p>Embedded devices such as 8-bit microcontrollers like the Arduino can run Woopsa too! The Woopsa Embedded library was created with strong memory constrains in mind and only uses about 2-3 kiloBytes of RAM. This library is designed to be 100% cross-platform and only requires your microcontroller to support basic string functions and the C language.</p>
<h2 id="embedded-server-getting-the-code">Getting the code</h2>
<p>Start off by <a href="/download/">downloading</a> Woopsa and get the 3 Woopsa source files (<samp>woopsa-config.h</samp>, <samp>woopsa-server.h</samp> and <samp>woopsa-server.c</samp>) in the Embedded folder. Add these files to your project and include <samp>woopsa-server.h</samp> at the top of your source file:</p>
<div class="code">
{% highlight c %}
#include "woopsa-server.h"
{% endhighlight %}
</div>
<p>Depending on which platform you're trying to port Woopsa, you might have to make some adjustments to <samp>woopsa-config.h</samp>. For example, the version that is provided assumes that the standard string processing C libraries are available. This means that functions such as <samp>snprintf</samp>, <samp>strstr</samp>, <samp>strlen</samp>, <samp>atoi</samp> or <samp>tolower</samp> are available.</p>
<p>The Woopsa Embedded server uses macros defined in that file to convert strings to integers, count the length of strings, etc. In case your system does not support these functions, you can always write replacement functions adapted to your platform.</p>
<p class="note"><strong>Note:</strong> You can also disable strings and remote method invocation alltogether if you do not need these features in your embedded Woopsa server. Just comment the <samp>#define</samp>s called <samp>WOOPSA_ENABLE_STRINGS</samp> and <samp>WOOPSA_ENABLE_METHODS</samp>.</p>
<h2 id="embedded-server-how-to-use-woopsa-embedded">How to use Woopsa Embedded</h2>
<p>The Woopsa Embedded server is fully cross-platform because it is not tied to any specific network implementation. This means you will have to write the networking code yourself. You must thus write whatever code is necessary to aquire a buffer of bytes send from a client (which will be an HTTP request), then pass this buffer to the <samp>WoopsaHandleRequest</samp> function along with a response buffer. This function will analyze the request and build a response in the buffer. You can then send this buffer back to the client using whatever code you have.</p>
<p>To publish data using the Woopsa Embedded server, you use the simple macros defined in <samp>woopsa-server.h</samp> to specify properties and methods to publish.</p>
<div class="code">
{% highlight c %}
float Temperature = 24.2;
char IsRaining = 1;
int Altitude = 430;
float Sensitivity = 0.5;
char City[20] = "Geneva";
float TimeSinceLastRain = 11;
char weatherBuffer[20];
char* GetWeather() {
sprintf(weatherBuffer, "sunny");
return weatherBuffer;
}
WOOPSA_BEGIN(woopsaEntries)
WOOPSA_PROPERTY_READONLY(Temperature, WOOPSA_TYPE_REAL)
WOOPSA_PROPERTY(IsRaining, WOOPSA_TYPE_LOGICAL)
WOOPSA_PROPERTY(Altitude, WOOPSA_TYPE_INTEGER)
WOOPSA_PROPERTY(Sensitivity, WOOPSA_TYPE_REAL)
WOOPSA_PROPERTY(City, WOOPSA_TYPE_TEXT)
WOOPSA_PROPERTY(TimeSinceLastRain, WOOPSA_TYPE_TIME_SPAN)
WOOPSA_METHOD(GetWeather, WOOPSA_TYPE_TEXT)
WOOPSA_END;
{% endhighlight %}
</div>
<p>This particular piece of code will create a new dictionary of Woopsa entries called <samp>woopsaEntries</samp>. The macro takes care of creating the dictionary so you do not need to "initialize" the variable.</p>
<p>Before you can pass data to Woopsa Embedded, you must first initialize a Woopsa server. To do this, just call the <samp>WoopsaServerInit</samp> function and pass it a pointer to a <samp>WoopsaServer</samp>, a path prefix, a dictionary of entries and an optional request handling method (see <a href="#embedded-server-serving-other-content">Serving other content</a>).</p>
<div class="code">
{% highlight c %}
WoopsaServer server;
..
WoopsaServerInit(&server, "/woopsa/", woopsaEntries, NULL);
{% endhighlight %}
</div>
<p>That's it! You're now ready to pass data to Woopsa Embedded. It's up to you to write code to listen on a specific port or IP using the library of your choice (whatever is available on your embedded target). Because Woopsa was designed to be used by browsers directly, we recommend listening on TCP port 80, but nothing prevents you from passing Woopsa through a serial port, for example.</p>
<p>Because requests made through HTTP can be fragmented across multiple TCP packets, use the <samp>WoopsaCheckRequestComplete</samp> function, passing it your buffer, to check if you need to aquire more bytes from the client. This function will return <samp>WOOPSA_REQUEST_COMLETE</samp> when the full HTTP request has been received (including POST data), or <samp>WOOPSA_REQUEST_MORE_DATA_NEEDED</samp> if you need to get more bytes.</p>
<p>Once the request is complete, you can simply call the <samp>WoopsaHandleRequest</samp> function, passing it your input and output buffers. This function will generate a Woopsa response and returns <samp>WOOPSA_SUCCESS</samp> when everything went well. In cases where the client makes a malformed request or requests an element that doesn't exist, <samp>WOOPSA_CLIENT_REQUEST_ERROR</samp> will be returned and the buffer will contain a 404 or 500 HTTP error, which you must send back to the client.</p>
<p>You can then send the output buffer as a response to the client, and happily wait until the next request!</p>
<div class="code">
{% highlight c %}
char buffer[2048]; // A buffer to store requests and responses
WoopsaUInt16 responseLength; // An integer that will be used to store the response length
...
// Accept an incoming connection and fill the buffer with the request
if (WoopsaCheckRequestComplete(&server, buffer, sizeof buffer) != WOOPSA_REQUEST_COMLETE) {
// Get more bytes, this request is not finished
} else {
if (WoopsaCheckRequestComplete(&server, buffer, sizeof buffer, buffer, sizeof buffer, &responseLength) >= WOOPSA_SUCCESS) {
// Send the buffer back to the client
}
}
{% endhighlight %}
</div>
<p class="note"><strong>Note:</strong> Woopsa Embedded was designed with high portability and low memory in mind. This means you can safely pass the same buffer for request and response, limiting your memory usage greatly.</p>
<h2 id="embedded-server-creating-your-first-server">Creating your first server</h2>
<h3 id="embedded-server-arduino-ethernet-shield-2-example">Arduino Ethernet Shield 2 example</h3>
<p>A sample is available in the source files of Woopsa that runs on the Arduino Mega with an Ethernet Shield 2. Networking on the Arduino Ethernet Shield is easy and a shorter version of the code is given below to help you get started:</p>
<div class="code">
{% highlight c %}
#include <Ethernet2.h>
#include "woopsa-server.h"
byte mac[] = {
0x90, 0xA2, 0xDA, 0x10, 0x32, 0x0B
};
IPAddress ip(192, 168, 42, 3);
EthernetServer server(80);
WoopsaServer woopsaServer;
float Temperature;
int Altitude;
WOOPSA_BEGIN(woopsaEntries)
WOOPSA_PROPERTY(Temperature, WOOPSA_TYPE_REAL)
WOOPSA_PROPERTY(Altitude, WOOPSA_TYPE_INTEGER)
WOOPSA_END
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial); // wait for serial port to connect. Needed for native USB port only
// start the Ethernet connection and the server:
Ethernet.begin(mac, ip);
server.begin();
WoopsaServerInit(&woopsaServer, "/woopsa/", woopsaEntries, NULL);
}
void loop() {
int bufferAt = 0;
char dataBuffer[2048];
short unsigned int responseLength;
memset(dataBuffer, 0, sizeof(dataBuffer));
// Listen for incoming clients
EthernetClient client = server.available();
if (client) {
while (client.connected()) {
if (client.available()) {
// We _append_ data to our buffer when it is received
bufferAt += client.read((unsigned char*)(dataBuffer + bufferAt), sizeof(dataBuffer));
// When we get a request from a client, we need
// to make sure it's complete before we pass it
// to the Woopsa server. This allows us to handle
// cases where packets are fragmented.
if (WoopsaCheckRequestComplete(&woopsaServer, dataBuffer, sizeof(dataBuffer)) != WOOPSA_REQUEST_COMLETE) {
continue;
}
if ( WoopsaHandleRequest(&woopsaServer, dataBuffer, sizeof(dataBuffer), dataBuffer, sizeof(dataBuffer), &responseLength) >= WOOPSA_SUCCESS ) {
client.print(dataBuffer);
}
break;
}
}
}
client.stop();
}
{% endhighlight %}
</div>
<h3 id="embedded-server-standard-sockets-example">Standard sockets example</h3>
<p>A sample is available in the source files of Woopsa that should run on any flavor of Linux/UNIX-like system. A shorter version of the code is given below to help you get started quickly:</p>
<div class="code">
{% highlight c %}
float Temperature = 24.2;
int Altitude = 430;
WOOPSA_BEGIN(woopsaEntries)
WOOPSA_PROPERTY(Temperature, WOOPSA_TYPE_REAL)
WOOPSA_PROPERTY(Altitude, WOOPSA_TYPE_INTEGER)
WOOPSA_END;
int main(int argc, char argv[]) {
SOCKET sock, clientSock;
struct sockaddr_in addr, clientAddr;
char buffer[BUFFER_SIZE];
int clientAddrSize = 0, readBytes = 0;
WoopsaServer server;
WoopsaUInt16 responseLength;
memset(buffer, 0, sizeof(buffer));
WoopsaServerInit(&server, "/woopsa/", woopsaEntries, NULL);
sock = socket(AF_INET, SOCK_STREAM, 0);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(WOOPSA_PORT);
bind(sock, (struct sockaddr *)&addr, sizeof(addr));
listen(sock, 5);
while (1) {
clientAddrSize = sizeof(struct sockaddr_in);
clientSock = accept(sock, (struct sockaddr *)&clientAddr, (socklen_t*)&clientAddrSize);
while (1) {
readBytes = recv(clientSock, buffer + readBytes, sizeof(buffer), NULL);
if (readBytes == 0)
break;
if (WoopsaCheckRequestComplete(&server, buffer, sizeof(buffer)) != WOOPSA_REQUEST_COMLETE) {
// If the request is not complete, it means more data needs
// to be -added- to the buffer
continue;
}
if (WoopsaHandleRequest(&server, buffer, sizeof(buffer), buffer, sizeof(buffer), &responseLength) >= WOOPSA_SUCCESS) {
send(clientSock, buffer, responseLength, NULL);
}
readBytes = 0;
memset(buffer, 0, sizeof(buffer));
}
}
}
{% endhighlight %}
</div>
<h2 id="embedded-server-serving-other-content">Serving other content</h2>
<p>Upon Woopsa server initialization, you can optionally pass a function pointer to handle requests which are not in the woopsa path prefix. This means you can also serve some HTML or other data directly on your embedded system!
{% highlight c %}
WoopsaUInt16 ServeHTML(WoopsaChar8 path[], WoopsaUInt8 isPost, WoopsaChar8 dataBuffer[], WoopsaUInt16 dataBufferSize) {
strcpy(dataBuffer, "Hello world!");
return strlen("Hello world!");
}
..
WoopsaServerInit(&server, "/woopsa/", woopsaEntries, ServeHTML);
{% endhighlight %}
<p class="note"><strong>Note:</strong> You can do whatever you want in this function, and don't even necessarily have to copy data to the data buffer. The only mandatory step is to return the length of the content you will be serving, so Woopsa can prepare the proper Content-Length headers. When your function is called, the <samp>WoopsaHandleRequest</samp> function returns <samp>WOOPSA_OTHER_RESPONSE</samp>, allowing you to write something to your client directly. This is useful if you wish to serve some really long piece of data from program memory without being constrained by your RAM.</p>
<h2 id="embedded-server-limitations">Limitations</h2>
<p>Because Woopsa Embedded was designed with extreme portability and low memory usage in mind, it does not support subscriptions (publish/subscribe) or multiple requests. Also, the DateTime type is not handled in any way, as a large date-handling library would be necessary. Finally, Woopsa methods can not have any arguments: only return values are allowed. However, this should be enough to cover most embedded use cases. If you need more, you're welcome to use an embedded system running an OS like Linux and use the .NET library with Mono.</p>
</div>
<div id="javascript-client" class="part-block">
<h1>Using the JavaScript client</h1>
<h2>Introduction</h2>
<p>Our official JavaScript client is the easiest way to get started using Woopsa in a web application. Contrary to most other protocols out there, you <strong>do not need a gateway server</strong>. Indeed, because Woopsa is based on HTTP, browsers can directly connect to Woopsa servers. This is great in many aspects, because it means there are less possible points of failure which makes for a much simpler infrastructure!</p>
<h2 id="javascript-client-getting-the-code">Getting the code</h2>
<p class="note"><strong>Note:</strong> The Woopsa JavaScript library depends on <a href="https://jquery.com/">jQuery</a> for making asynchronous requests.</p>
<dl>
<dt>Using a content delivery network</dt>
<dd><p>Woopsa will soon be available on a CDN. In the meantime, we suggest you host it yourself.</p></dd>
<!--
<dd><p>CDNs (Content Delivery Networks) assures you have the latest version of the Woopsa client library at all times. If you want to go this route, simply paste the following code before the closing <samp></body></samp> tag of your page</p>
{% highlight html %}
<script type="text/javascript" src="http://coming-soon/woopsa-client.min.js"/></script>
{% endhighlight %}
</dd>
-->
<dt>Hosting it yourself</dt>
<dd><p>If you'd prefer to host the Woopsa client code yourself, simply <a href="/download/">download</a> Woopsa and get the minified (compressed) JavaScript in the JavaScript folder. Include it before the closing <samp></body></samp> tag of your page.</p>
{% highlight html %}
<script type="text/javascript" src="{your-javascript-folder}/woopsa-client.min.js"/>
{% endhighlight %}
</dd>
</dl>
<h2 id="javascript-client-creating-your-first-client">Creating your first client</h2>
<p class="note"><strong>Note:</strong> Woopsa is connection-less. This means that creating a client does not initiate any data exchange until requests are actually made.</p>
<p>Creating a new Woopsa client is easy. You just need to know the URL of the server, and pass jQuery as a dependency. Then you're ready to make your first requests! Because we want to help you get started as quickly as possible, we host a free sample Woopsa server on <samp>http://demo.woopsa.org/woopsa</samp>. To tell Woopsa to communicate with this server, use the following code to create a new client:</p>
<div class="code">
{% highlight javascript %}
var client = new WoopsaClient("http://demo.woopsa.org/woopsa", jQuery);
{% endhighlight %}
</div>
<p>That's it! You're now ready to make your first requests.</p>
<h2 id="javascript-client-basic-usage">Basic usage</h2>
<p class="note"><strong>Note:</strong> our JavaScript library implements the Promise interface, which means <samp>done</samp>, <samp>fail</samp> and <samp>always</samp> are available for you!</p>
<p>Woopsa has 4 basic verbs that allow you to do everything you need! The three verbs are presented below, along with the code examples that go with them:</p>
<h3 id="javascript-client-exploring-the-object-tree-with-meta">Exploring the object tree with <samp>meta</samp></h3>
<p>Because Woopsa is object-oriented, you can freely explore the data that's available on the server. This allows you to write very flexible clients that can fetch data based on what they're presented with. Fetching the object hierarchy is as simple as specifying the path and a callback method:</p>
<div class="code">
{% highlight javascript %}
client.meta("/", function (tree){
console.log(tree);
});
{% endhighlight %}
</div>
<div class="output">
{% highlight json %}
{
"Name": "WeatherStation",
"Items": [
"SubscriptionService",
"Thermostat"
],
"Properties": [
{
"Name": "Temperature",
"Type": "Real",
"ReadOnly": true
},
{
"Name": "IsRaining",
"Type": "Logical",
"ReadOnly": false
},
{
"Name": "Altitude",
"Type": "Integer",
"ReadOnly": false
},
{
"Name": "Sensitivity",
"Type": "Real",
"ReadOnly": false
},
{
"Name": "City",
"Type": "Text",
"ReadOnly": false
},
{
"Name": "Time",
"Type": "DateTime",
"ReadOnly": false
},
{
"Name": "TimeSinceLastRain",
"Type": "TimeSpan",
"ReadOnly": false
}
],
"Methods": [
{
"Name": "MultiRequest",
"ReturnType": "JsonData",
"ArgumentInfos": [
{
"Name": "Requests",
"Type": "JsonData"
}
]
},
{
"Name": "GetWeatherAtDate",
"ReturnType": "Text",
"ArgumentInfos": [
{
"Name": "date",
"Type": "DateTime"
}
]
},
{
"Name": "ToString",
"ReturnType": "Text",
"ArgumentInfos": []
},
{
"Name": "GetHashCode",
"ReturnType": "Integer",
"ArgumentInfos": []
}
]
}
{% endhighlight %}
</div>
<p>If you want to learn more about the meaning of all this stuff, read the <a href="/specifications/">full specifications</a> to the Woopsa protocol!</p>
<h3 id="javascript-client-reading-a-property-with-read">Reading a property with <samp>read</samp></h3>
<p>You can make a request to read a property by using the simple <samp>read</samp> function provided to you in the library. This method only requires 2 parameters: the path of the property and a callback method for when the value is returned by the server.</p>
<div class="code">
{% highlight javascript %}
client.read("/Temperature", function (value){
console.log("The temperature is " + value);
});
{% endhighlight %}
</div>
<div class="output">
{% highlight text %}
The temperature is 24.2
{% endhighlight %}
</div>
<h3 id="javascript-client-writing-to-a-property-with-write">Writing to a property with <samp>write</samp></h3>
<p>Writing properties is as easy as reading them. Watch out though, because properties have a type and may be read-only! The write method requires 3 parameters: the path of the property, the value to write and a callback function (optional) for when the value is writting to the server.</p>
<div class="code">
{% highlight javascript %}
client.write("/Sensitivity", 0.5, function (response){
if ( response == true ){
console.log("The value was written successfully!");
}else{
console.log("The value was not written successfully :(");
}
})
{% endhighlight %}
</div>
<div class="output">
{% highlight text %}
The value was written successfully!
{% endhighlight %}
</div>
<h3 id="javascript-client-calling-a-method-function-with-invoke">Calling a method/function with <samp>invoke</samp></h3>
<p>Methods in Woopsa allow you to send commands to the server in a more intuitive way. Again, this is very easily done with Woopsa. The invoke method requires 3 parameters: the path of the method, the arguments and a callback for when the method has been called on the server.</p>
<div class="code">
{% highlight javascript %}
client.invoke("/GetWeatherAtDate", new Date(2015, 9, 6), function (response){
console.log(response);
})
{% endhighlight %}
</div>
<div class="output">
{% highlight text %}
partly cloudy
{% endhighlight %}
</div>
<h2 id="javascript-client-listening-for-value-changes">Listening for value changes (publish/subscribe)</h2>
<p>On servers that support it, a <samp>SubscriptionService</samp> object is available at the root. This object has various methods to subscribe to value changes. However, the library abstracts this away for you! An easy-to-use <samp>onChange</samp> method allows you to bind a callback to the change of a property. All you need to provide it is a path and a callback, as well as a monitorInterval and a publishInterval.</p>
<div class="code">
{% highlight javascript %}
var newSubscription;
client.onChange("/Temperature", function (value){
console.log(value);
}, 0.03, 0.03, function (subscription){
newSubscription = subscription;
}
...
// When you wish to unregister this subscription to stop receiving notifications for this property...
newSubscription.unregister(function (){
console.log("Subscription removed");
});
{% endhighlight %}
</div>
<div class="output">
{% highlight text %}
24.2
..
24.3
..
24.1
..
{% endhighlight %}
</div>
<p>If you wish to have more control over the rate at which events are raised, or just want to learn a bit more about how the publish/subscribe pattern is implemented in Woopsa, go read the <a href="/specifications/">full specifications</a>! </p>
<h3 id="javascript-client-using-ssl-tls">Using SSL/TLS</h3>
<p>Under the hood, the Woopsa JavaScript client uses the standard XmlHttpRequest. This means that using it with an SSL/TLS enabled server is just a matter of using <samp>https://</samp> instead of <samp>http://</samp> when specifying the server URL.</p>
{% highlight csharp %}
var client = new WoopsaClient("https://demo.woopsa.org/woopsa");
{% endhighlight %}
<h3 id="javascript-client-using-authentication">Using authentication</h3>
<p>If the Woopsa server you are communicating with requires client authentication with a username and password, simply set the <samp>username</samp> and <samp>password</samp> properties of your client and all future requests will be authenticated.</p>
{% highlight javascript %}
var client = new WoopsaClient("http://demo.woopsa.org/woopsa", jQuery);
client.username = "admin";
client.password = "password";
{% endhighlight %}
</div>
<div id="csharp-client" class="part-block">
<h1>Using the C# / .NET client</h1>
<h2 id="csharp-client-introduction">Introduction</h2>
<p>The C# / .NET clients are included in the <samp>Woopsa</samp> assembly. We created the Woopsa library with Mono in mind, trying to use only the most standard .NET components for maximum compatibility. Like the JavaScript client, <strong>you do not need a gateway server</strong> to relay information. Just directly connect to your Woopsa server! There is a "standard" and a dynamic client (available only in .NET versions that support reflection). This tutorial covers the standard version of the client and finishes with an example on how to use the dynamic client.</p>
<h2 id="csharp-client-getting-the-assembly-and-using-it">Getting the assembly and using it</h2>
<p>Start off by <a href="/download/">downloading</a> Woopsa and get the Woopsa.dll assembly inside the DotNet folder. Add this assembly as a reference in your Visual Studio project and add the following line to the top of your source file:</p>
<div class="code">
{% highlight csharp %}
using Woopsa;
{% endhighlight %}
</div>
<p>That's it! You're now ready to create your client.</p>
<h2 id="csharp-client-creating-your-first-client">Creating your first client</h2>
<p class="note"><strong>Note:</strong> Woopsa is connection-less. This means that creating a client does not initiate any data exchange until requests are actually made.</p>
<p>Creating a new Woopsa client is easy. You just need to know the URL of the server. Then you're ready to make your first requests! Because we want to help you get started as quickly as possible, we host a free sample Woopsa server on <samp>http://demo.woopsa.org/woopsa</samp>. To tell Woopsa to communicate with this server, use the following code to create a new client:</p>
<div class="code">
{% highlight csharp %}
WoopsaClient client = new WoopsaClient("http://demo.woopsa.org/woopsa");
{% endhighlight %}
</div>
<p>That's it! You're now ready to work with your server.</p>
<h2 id="csharp-client-basic-usage">Basic usage</h2>
<p>The C# / .NET library abstracts away a lot of the workings of Woopsa. It works differently from the JavaScript implementation, making use of all the powerful features that the C# language and .NET platform have to offer.</p>
<h3 id="csharp-client-exploring-the-object-tree">Exploring the object tree</h3>
<p>The method <samp>Meta("path")</samp> returns a <samp>WoopsaMetaResult</samp> object containing all the information on the object located at the given <samp>path</samp> parameter.</p>
<div class="code">
{% highlight csharp %}
WoopsaMetaResult meta = client.ClientProtocol.Meta("/");
foreach (var property in meta.Properties)
{
Console.WriteLine("Property {0} : {1} ({2})", property.Name, property.Type, property.IsReadOnly);
}
foreach (var method in meta.Methods)
{
Console.WriteLine("Method {0} : {1}", method.Name, method.ReturnType);
foreach (WoopsaMethodArgumentInfoMeta argument in method.ArgumentInfos)
{
Console.WriteLine("Argument {0} : {1}", argument.Name, argument.Type);
}
}
foreach (var obj in meta.Items)
{
Console.WriteLine("Item {0}", obj);
}
{% endhighlight %}
</div>
<div class="output">
{% highlight text %}
Property Temperature : Real (True)
Property IsRaining : Logical (False)
Property Altitude : Integer (False)
Property Sensitivity : Real (False)
Property City : Text (False)
Property Time : DateTime (False)
Property TimeSinceLastRain : TimeSpan (False)
Method MultiRequest : JsonData
Argument Requests : JsonData
Method GetWeatherAtDate : Text
Argument date : DateTime
Item SubscriptionService
Item Thermostat
{% endhighlight %}
</div>
<h3 id="csharp-client-reading-a-property">Reading a property</h3>
<p>Because they implement the <samp>IWoopsaProperty</samp> interface, reading a value is as easy as using the <samp>Read</samp> method! The library takes care of most cases of typecasting.</p>
<div class="code">
{% highlight csharp %}
Console.WriteLine("Temperature = {0}", client.ClientProtocol.Read("Temperature"));
{% endhighlight %}
</div>
<div class="output">
{% highlight text %}
Temperature = 24.2
{% endhighlight %}
</div>
<h3 id="csharp-client-writing-to-a-property">Writing to a property</h3>
<p>When you wish to write a property, you need to know its type in order to assign it a <samp>WoopsaValue</samp>. Once you know this, writing is as simple as assigning something with the <samp>Write</samp> method.</p>
<div class="code">
{% highlight csharp %}
client.ClientProtocol.Write("Sensitivity", new WoopsaValue(0.5));
{% endhighlight %}
</div>
<p class="note"><strong>Note:</strong> The Woopsa .NET library comes with tons of premade <samp>WoopsaValue</samp> constructors that accept just about any type you can think of, making your life easier when dealing with <samp>WoopsaValue</samp>s.</p>
<h3 id="csharp-client-calling-a-method-function">Calling a method/function</h3>
<p>When you have your method, simply call the <samp>Invoke</samp> method and passing it a collection of <samp>NameValueCollection</samp>s for the arguments.
<div class="code">
{% highlight csharp %}
NameValueCollection arguments = new NameValueCollection();
arguments.Add("date",new WoopsaValue(DateTime.Now).AsText);
string weather = client.ClientProtocol.Invoke("GetWeatherAtDate", arguments);
Console.WriteLine(weather);
{% endhighlight %}
</div>
<div class="output">
{% highlight text %}
cloudy
{% endhighlight %}