-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added a Demo Server + Client in .NET + first Release!
- Loading branch information
1 parent
d81030c
commit 349719c
Showing
19 changed files
with
631 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
bin | ||
obj | ||
/*.suo |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
(function (global){ | ||
var nop = function () {}; | ||
|
||
var Subscription = function (channel, path, onChangeCallback, successCallback, monitorInterval, publishInterval) { | ||
this.channel = channel; | ||
this.path = path; | ||
this.onChange = onChangeCallback || nop; | ||
|
||
this.subscriptionId = null; | ||
|
||
monitorInterval = monitorInterval || 100; | ||
publishInterval = publishInterval || 200; | ||
successCallback = successCallback || nop; | ||
|
||
this.unregister = function (successCallback) { | ||
return channel.client.invoke( | ||
"/SubscriptionService/UnregisterSubscription", | ||
{ | ||
SubscriptionChannel: channel.channelId, | ||
SubscriptionId: this.subscriptionId | ||
}, | ||
(function (result) { | ||
successCallback(); | ||
}).bind(this) | ||
); | ||
} | ||
|
||
this.register = function (callback){ | ||
return channel.client.invoke( | ||
"/SubscriptionService/RegisterSubscription", | ||
{ | ||
SubscriptionChannel: channel.channelId, | ||
PropertyLink: path, | ||
MonitorInterval: monitorInterval, | ||
PublishInterval: publishInterval | ||
}, | ||
(function (result) { | ||
this.subscriptionId = result; | ||
successCallback(); | ||
if ( callback ){ | ||
callback(this); | ||
} | ||
}).bind(this) | ||
); | ||
} | ||
|
||
this.register.call(this); | ||
}; | ||
|
||
var SubscriptionChannel = function (client, notificationQueueSize, onCreateCallback) { | ||
this.client = client; | ||
this.onCreate = onCreateCallback; | ||
|
||
this.channelId = null; | ||
this.subscriptions = []; | ||
this.listenStarted = false; | ||
this.subscriptionsDictionary = {}; | ||
|
||
var created = false; | ||
|
||
this.registerSubscription = function (path, onChangeCallback, successCallback, monitorInterval, publishInterval) { | ||
var newSubscription = new Subscription(this, path, onChangeCallback, successCallback, monitorInterval, publishInterval); | ||
this.subscriptions.push(newSubscription); | ||
if ( !this.listenStarted ) { | ||
this.listenStarted = true; | ||
waitNotification.call(this); | ||
} | ||
this.subscriptionsDictionary[path] = newSubscription; | ||
return newSubscription; | ||
}; | ||
|
||
this.unregisterSubscription = function (subscription, successCallback) { | ||
var subscriptionIndex = this.subscriptions.indexOf(subscription); | ||
if ( subscriptionIndex != -1 ) { | ||
this.subscriptions[subscriptionIndex].unregister((function (subscriptionIndex, successCallback){ | ||
this.subscriptions.splice(subscriptionIndex, 1); | ||
successCallback(); | ||
}).bind(this, subscriptionIndex, successCallback)) | ||
} | ||
} | ||
|
||
this.register = function (){ | ||
return client.invoke( | ||
"/SubscriptionService/CreateSubscriptionChannel", | ||
{NotificationQueueSize: notificationQueueSize}, | ||
(function (result){ | ||
this.channelId = result; | ||
if ( !created ) | ||
this.onCreate.call(this, this.channelId); | ||
created = true; | ||
}).bind(this) | ||
); | ||
} | ||
|
||
this.register.call(this); | ||
|
||
var retryFrequency = 10000; //The frequency at which to retry connecting, in milliseconds | ||
|
||
var waitNotification = function () { | ||
return client.invoke( | ||
"/SubscriptionService/WaitNotification", | ||
{SubscriptionChannel: this.channelId}, | ||
(function (notifications) { | ||
for ( var i = 0; i < notifications.length; i++ ) { | ||
var notification = notifications[i]; | ||
if ( this.subscriptionsDictionary[notification.PropertyLink.Value] ) { | ||
this.subscriptionsDictionary[notification.PropertyLink.Value].onChange(notification.Value.Value); | ||
} | ||
} | ||
waitNotification.call(this); | ||
}).bind(this) | ||
).fail(function (request, status, error){ | ||
if ( request.readyState == 4 ){ | ||
//We have reconnected but the server has forgotten our channel | ||
this.register().done(function (){ | ||
for ( var i = 0; i < this.subscriptions.length; i++ ) { | ||
this.subscriptions[i].register() | ||
} | ||
}.bind(this)) | ||
} | ||
setTimeout((function (){ | ||
waitNotification.call(this); | ||
}).bind(this), retryFrequency); | ||
}.bind(this)); | ||
} | ||
}; | ||
|
||
global.WoopsaClient = function (url, jQuery) { | ||
var $ = jQuery; | ||
var subscriptionChannel = null; | ||
var errorCallbacks = []; | ||
|
||
if ( url.lastIndexOf('/') == url.length-1 ) { | ||
this.url = url; | ||
} else { | ||
this.url = url + "/"; | ||
} | ||
|
||
this.read = function (path, callback) { | ||
return $.get(this.url + "read" + path, {}, function (data, textStatus, jqXHR) { | ||
callback(data.Value, path); | ||
}).fail(function (request, status, errorThrown){ | ||
raiseError(status, errorThrown); | ||
}); | ||
}; | ||
|
||
this.write = function (path, value, callback) { | ||
return $.post(this.url + "write" + path, {value: value}, function (data, textStatus, jqXHR) { | ||
callback(true, path); | ||
}) | ||
.fail(function (request, status, errorThrown){ | ||
raiseError(status, errorThrown); | ||
}); | ||
}; | ||
|
||
this.meta = function (path, callback) { | ||
return $.get(this.url + "meta" + path, {}, function (data, textStatus, jqXHR) { | ||
callback(data, path); | ||
}) | ||
.fail(function (request, status, errorThrown){ | ||
raiseError(status, errorThrown); | ||
}); | ||
}; | ||
|
||
this.invoke = function (path, arguments, callback, timeout) { | ||
timeout = timeout || 10000; | ||
return $.ajax({ | ||
type: 'POST', | ||
timeout: timeout, | ||
url: this.url + "invoke" + path, | ||
data: arguments | ||
}) | ||
.done(function (data){ | ||
callback(data.Value); | ||
}) | ||
.fail(function (request, status, errorThrown){ | ||
raiseError(status, errorThrown); | ||
}); | ||
}; | ||
|
||
this.createSubscriptionChannel = function (notificationQueueSize, callback) { | ||
if ( subscriptionChannel != null ) { | ||
callback.call(subscriptionChannel, subscriptionChannel.channelId); | ||
return; | ||
} | ||
var newChannel = new SubscriptionChannel(this, notificationQueueSize, function (channelId){ | ||
callback.call(this, channelId); | ||
}) | ||
subscriptionChannel = newChannel; | ||
return newChannel; | ||
}; | ||
|
||
this.onChange = function (path, callback, monitorInterval, publishInterval) { | ||
if ( subscriptionChannel == null ) { | ||
this.createSubscriptionChannel(200, (function (){ | ||
this.onChange(path, callback, monitorInterval, publishInterval); | ||
}).bind(this)) | ||
}else{ | ||
subscriptionChannel.registerSubscription(path, callback, nop, monitorInterval, publishInterval); | ||
} | ||
}; | ||
|
||
this.onError = function (callback){ | ||
errorCallbacks.push(callback); | ||
}; | ||
|
||
function raiseError(type, errorThrown){ | ||
for ( var i = 0; i < errorCallbacks.length; i++ ) { | ||
errorCallbacks[i](type, errorThrown); | ||
} | ||
} | ||
}; | ||
})(window); |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
devenv ..\Woopsa.sln /build Release /project Woopsa |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
<?xml version="1.0" encoding="utf-8" ?> | ||
<configuration> | ||
<startup> | ||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> | ||
</startup> | ||
</configuration> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using Woopsa; | ||
|
||
namespace WoopsaDemoClient | ||
{ | ||
class Program | ||
{ | ||
static void Main(string[] args) | ||
{ | ||
Console.WriteLine(" *** Welcome to the Woopsa Demo Client! *** "); | ||
Console.WriteLine(" Note: read the source code to understand what's happening behind the scenes!"); | ||
Console.WriteLine(""); | ||
Console.Write("Please enter the Woopsa server URL or leave blank for default (http://localhost/woopsa): "); | ||
|
||
string serverUrl = Console.ReadLine(); | ||
if (serverUrl == "") | ||
serverUrl = "http://localhost/woopsa"; | ||
|
||
WoopsaClient client = new WoopsaClient(serverUrl); | ||
|
||
Console.WriteLine("Woopsa client created on URL: {0}", serverUrl); | ||
|
||
// Display all properties and their values | ||
Console.WriteLine("Properties and their values:"); | ||
foreach (WoopsaClientProperty property in client.Root.Properties) | ||
{ | ||
Console.WriteLine(" * {0} : {1} = {2}", property.Name, property.Type, property.Value); | ||
|
||
// As an example, if we find a PropertyInteger value (like in the demo server), | ||
// we change its value to 1. That way you can see how it's done using the | ||
// standard client | ||
if (property.Name == "PropertyInteger") | ||
{ | ||
// Create a subscription for example's sake | ||
Console.WriteLine(" => Creating a subscription"); | ||
property.Change += property_Change; | ||
|
||
// Actually change the value | ||
Console.WriteLine(" => Changing value to 1"); | ||
property.Value = new WoopsaValue(1); | ||
} | ||
} | ||
|
||
// Display methods and their arguments | ||
Console.WriteLine("Methods and their arguments:"); | ||
foreach (WoopsaMethod method in client.Root.Methods) | ||
{ | ||
// Display the method | ||
Console.WriteLine(" * {0} : {1}", method.Name, method.ReturnType); | ||
foreach (WoopsaMethodArgumentInfo argumentInfo in method.ArgumentInfos) | ||
{ | ||
Console.WriteLine(" * {0} : {1}", argumentInfo.Name, argumentInfo.Type); | ||
} | ||
|
||
// As an example, if we find a SayHello method (like in the demo server), | ||
// we call it. That way you can see how to call methods using the standard | ||
// client! | ||
if (method.Name == "SayHello") | ||
{ | ||
Console.WriteLine(" => SayHello found! Calling it now..."); | ||
Console.WriteLine(" => Result = {0}", method.Invoke(new List<IWoopsaValue>() | ||
{ | ||
new WoopsaValue("Woopsa Demo Client") | ||
}) | ||
); | ||
} | ||
} | ||
|
||
// Display embedded items and display its properties | ||
Console.WriteLine("Items:"); | ||
foreach (WoopsaClientObject item in client.Root.Items) | ||
{ | ||
// Display the item | ||
Console.WriteLine(" * {0}", item.Name); | ||
|
||
foreach(WoopsaClientProperty property in item.Properties) | ||
{ | ||
Console.WriteLine(" * {0} : {1} = {2}", property.Name, property.Type, property.Value); | ||
} | ||
} | ||
|
||
// Leave the DOS window open | ||
Console.WriteLine("Press any key to exit..."); | ||
Console.ReadLine(); | ||
|
||
// Exit gracefully | ||
client.Dispose(); | ||
} | ||
|
||
static void property_Change(object sender, WoopsaNotificationEventArgs e) | ||
{ | ||
Console.WriteLine("Property {0} has changed, new value = {1}", (sender as WoopsaClientProperty).Name, e.Value); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
using System.Reflection; | ||
using System.Runtime.CompilerServices; | ||
using System.Runtime.InteropServices; | ||
|
||
// General Information about an assembly is controlled through the following | ||
// set of attributes. Change these attribute values to modify the information | ||
// associated with an assembly. | ||
[assembly: AssemblyTitle("WoopsaDemoClient")] | ||
[assembly: AssemblyDescription("")] | ||
[assembly: AssemblyConfiguration("")] | ||
[assembly: AssemblyCompany("Microsoft")] | ||
[assembly: AssemblyProduct("WoopsaDemoClient")] | ||
[assembly: AssemblyCopyright("Copyright © Microsoft 2015")] | ||
[assembly: AssemblyTrademark("")] | ||
[assembly: AssemblyCulture("")] | ||
|
||
// Setting ComVisible to false makes the types in this assembly not visible | ||
// to COM components. If you need to access a type in this assembly from | ||
// COM, set the ComVisible attribute to true on that type. | ||
[assembly: ComVisible(false)] | ||
|
||
// The following GUID is for the ID of the typelib if this project is exposed to COM | ||
[assembly: Guid("f2bd77d7-5671-4d9b-94ab-8c1a5c16168b")] | ||
|
||
// Version information for an assembly consists of the following four values: | ||
// | ||
// Major Version | ||
// Minor Version | ||
// Build Number | ||
// Revision | ||
// | ||
// You can specify all the values or you can default the Build and Revision Numbers | ||
// by using the '*' as shown below: | ||
// [assembly: AssemblyVersion("1.0.*")] | ||
[assembly: AssemblyVersion("1.0.0.0")] | ||
[assembly: AssemblyFileVersion("1.0.0.0")] |
Oops, something went wrong.