0004-Add-rsb-rpc-pattern_531bea6.patch

S. Barut, 10/01/2018 02:15 PM

Download (14.5 KB)

View differences:

rsb-cil-test/Program.cs
4 4
using Rsb;
5 5
using Rsb.Converter;
6 6
using Rsb.Transport.Socket;
7
using Rsb.Patterns;
7 8

  
8 9
using Rst.Kinematics;
9 10
using log4net.Config;
......
122 123
            repoInformer.Deactivate();
123 124
            Console.ReadKey();
124 125
            repoListener.Deactivate();
126

  
127
            Console.WriteLine("\nRPC pattern test");
128

  
129
            RemoteServer remote = new RemoteServer(new Scope("/test/methods"));
130
            remote.Activate();
131
            remote.timeout = 1000;
132

  
133
            LocalServer server = new LocalServer(new Scope("/test/methods"));
134
            server.AddMethod("echo", new Caller());
135

  
136
            //should have a timeout, since the LocalServer is not activated
137
            try
138
            {
139
                remote.Call<string, string>("echo", "hallo");
140
            }
141
            catch (RSBException e)
142
            {
143
                Console.WriteLine(e.Message);
144
            }
145
            
146
            server.Activate();
147
            // should work even though it is added after activation
148
            server.AddMethod("echo2", new Caller());
149

  
150
            Console.WriteLine("Echo : {0}", remote.Call<string, string>("echo", "hallo"));
151
            Console.WriteLine("Echo2: {0}", remote.Call<string, string>("echo2", "hallo"));
152

  
153
            Console.ReadKey();
154
            for (int i = 0; i < 20; i++) {
155
                Console.WriteLine("{0}: {1}", i, remote.Call<string, string>("echo", "hallo"));
156
            }
157

  
158
            Console.ReadKey();
159
            server.Deactivate();
160
            remote.Deactivate();
125 161
        }
126 162

  
163
	}
164
    
165
    public class Caller : DataCallback<string, string>
166
    {
167
        public override string Invoke(string data)
168
        {
169
            return data + data;
170
        }
127 171
    }
128 172
}
rsb-cil/Rsb/Factory.cs
113 113
            return CreateListener(new Scope(scope));
114 114
        }
115 115

  
116
        public RemoteServer CreateRemoteServer(Scope scope, int timeout = 3000)
117
        {
118
            var server = new RemoteServer(scope)
119
            {
120
                Timeout = timeout
121
            };
122
            return server;
123
        }
124

  
125
        public RemoteServer CreateRemoteServer(string scope, int timeout = 3000)
126
        {
127
            return CreateRemoteServer(new Scope(scope), timeout);
128
        }
129

  
130
        public LocalServer CreateLocalServer(Scope scope)
131
        {
132
            var server = new LocalServer(scope);
133
            return server;
134
        }
135

  
136
        public LocalServer CreateLocalServer(string scope)
137
        {
138
            return CreateLocalServer(new Scope(scope));
139
        }
140

  
116 141
    }
117 142
}
rsb-cil/Rsb/Patterns/DataCallback.cs
1

2
namespace Rsb.Patterns
3
{
4
    public abstract class DataCallback<ReplyType, RequestType> : ICallback
5
    {
6
        public Event InternalInvoke(Event e) {
7
            
8
            if (e.Method != "REQUEST") {
9
                throw new RSBException("Request expected!");
10
            }
11
            ReplyType result = this.Invoke((RequestType)e.Data);
12

  
13
            var reply = new Rsb.Event();
14
            reply.Method = "REPLY";
15
            reply.Scope = e.Scope;
16
            reply.AddCause(e.EventId);
17
            reply.Data = result;
18

  
19
            return reply;
20
        }
21

  
22
        public abstract ReplyType Invoke(RequestType param);
23
    }
24
    
25
}
rsb-cil/Rsb/Patterns/ICallback.cs
1

2
namespace Rsb.Patterns
3
{
4
    public interface ICallback
5
    {
6
        Event InternalInvoke(Event e);
7
    }
8
}
rsb-cil/Rsb/Patterns/LocalServer.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Linq;
4
using System.Text;
5
using System.Threading.Tasks;
6

  
7
namespace Rsb.Patterns
8
{
9
    public class LocalServer
10
    {
11
        private Scope scope;
12
        private Dictionary<string, Method> registeredMethods = new Dictionary<string, Method>();
13

  
14
        public bool IsActive {
15
            get; private set;
16
        }
17
        
18
        public LocalServer(Scope scope) {
19
            this.scope = scope;
20
            IsActive = false;
21
        }
22

  
23

  
24
        public void AddMethod(string name, ICallback callback) {
25
            if (registeredMethods.ContainsKey(name)) {
26
                throw new ArgumentException("method already registered");
27
            }
28
            Method method = new Method(scope, name, callback);
29
            registeredMethods.Add(name, method);
30
            if (IsActive) {
31
                method.Activate();
32
            }
33
        }
34

  
35
        public void Activate() {
36
            foreach (Method method in registeredMethods.Values) {
37
                method.Activate();
38
            }
39
            IsActive = true;
40
        }
41

  
42
        public void Deactivate() {
43
            foreach (Method method in registeredMethods.Values) {
44
                method.Deactivate();
45
            }
46
            IsActive = false;
47
        }
48
    }
49
}
rsb-cil/Rsb/Patterns/Method.cs
1

2
using System;
3

  
4
namespace Rsb.Patterns
5
{
6
    public class Method
7
    {
8
        private Informer informer;
9
        private Listener listener;
10
        private string name;
11
        public string Name { get => name; }
12

  
13
        private ICallback callback;
14

  
15
        public Method(Scope scope, string name, ICallback callback) {
16
            this.name = name;
17
            this.callback = callback;
18
            Scope work = scope.Concat(new Scope("/" + name));
19

  
20
            informer = Factory.Instance.createInformer(work);
21
            listener = Factory.Instance.createListener(work);
22

  
23
            listener.EventReceived += proceedCall;
24
        }
25

  
26
        private void proceedCall(Event theEvent)
27
        {
28
            // this is with the highest probability our own reply...
29
            if (theEvent.Method == "REPLY") {
30
                return;
31
            }
32
            // actually this would be handled in the ICallback too, but to make it more consistent...
33
            if (theEvent.Method != "REQUEST")
34
            {
35
                throw new RSBException("Request expected!");
36
            }
37

  
38
            informer.Send(callback.InternalInvoke(theEvent));
39
        }
40

  
41
        public void Activate() {
42
            informer.Activate();
43
            listener.Activate();
44
        }
45

  
46
        public void Deactivate()
47
        {
48
            informer.Deactivate();
49
            listener.Deactivate();
50
        }
51

  
52
    }
53
}
rsb-cil/Rsb/Patterns/RemoteMethod.cs
1
using System;
2
using System.Threading;
3
using Rsb;
4

  
5
namespace Rsb.Patterns
6
{
7
    public class RemoteMethod
8
    {
9
        Informer requestInformer;
10
        Listener replyListener;
11
        int timeout;
12

  
13
        object syncPrimitive;
14
        EventId requestId;
15
        Event result;
16

  
17
        public bool IsActive { get; private set; }
18

  
19
        public RemoteMethod(Scope scope, int timeout) {
20
            requestInformer = Factory.Instance.createInformer(scope);
21
            replyListener = Factory.Instance.createListener(scope);
22
            replyListener.EventReceived += handler;
23

  
24
            this.timeout = timeout;
25

  
26
            syncPrimitive = new object();
27
            IsActive = false;
28
        }
29

  
30
        private void handler(Event e) {
31
            if (requestId == null) {
32
                //maybe exception but actually we can just ignore...
33
                return;
34
            }
35

  
36
            if (e.Method == "REPLY" && e.IsCause(requestId))
37
            {
38
                
39
                // unlocking the SYNCHRONOUS call
40
                lock (syncPrimitive)
41
                {
42
                    result = e;
43
                    requestId = null;
44
                    Monitor.Pulse(syncPrimitive);
45
                }
46
            }
47
        }
48

  
49
        public Event Call(Event request) {
50
            if (!IsActive) {
51
                throw new InvalidOperationException("Participant not active");
52
            }
53

  
54
            bool timeout;
55
            // Locking until reply arrives (see handler above)
56
            lock (syncPrimitive)
57
            {
58
                requestId = request.EventId;
59
                requestInformer.Send(request);
60
                timeout = !Monitor.Wait(syncPrimitive, this.timeout);
61
            }
62
            
63
            if (timeout)
64
            {
65
                throw new RSBException(String.Format("Timeout after {0} milliseconds", this.timeout));
66
            }
67
            return result;
68
        }
69

  
70
        public void Activate() {
71
            requestInformer.Activate();
72
            replyListener.Activate();
73
            IsActive = true;
74
        }
75

  
76
        public void Deactivate() {
77
            requestInformer.Deactivate();
78
            replyListener.Deactivate();
79
            IsActive = false;
80
        }
81
    }
82
}
rsb-cil/Rsb/Patterns/RemoteServer.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Threading;
4

  
5
namespace Rsb.Patterns
6
{
7
    public class RemoteServer
8
    {
9
        public int Timeout = 3000; // milliseconds
10
        private Scope scope;
11

  
12
        public bool IsActive { get; private set; }
13

  
14
        Dictionary<string, RemoteMethod> connections = new Dictionary<string, RemoteMethod>();
15

  
16
        public RemoteServer(Scope scope) {
17
            this.scope = scope;
18
            IsActive = false;
19
        }
20

  
21
        public Event Call(string name, Event request) {
22
            Scope methodScope = scope.Concat(new Scope("/" + name));
23

  
24
            // checks if the request event is well-formed instead of well-forming the event
25
            if (!request.Method.Equals("REQUEST")) {
26
                throw new RSBException(String.Format("Request event expected! (Received: {0})", request.Method));
27
            }
28

  
29
            if (!request.Scope.Equals(methodScope)) {
30
                throw new RSBException(String.Format("Event is not the expected scope. (Expected: {0} | Got: {1})", methodScope, request.Scope));
31
            }
32

  
33
            RemoteMethod method;
34

  
35
            if (connections.ContainsKey(name)) {
36
                method = connections[name];
37
            }
38
            else
39
            {
40
                method = new RemoteMethod(methodScope, Timeout);
41
                connections.Add(name, method);
42
                if (IsActive) {
43
                    method.Activate();
44
                }
45
            }
46

  
47
            return method.Call(request);
48
        }
49

  
50
        public ReplyType Call<ReplyType, RequestType>(string name, RequestType requestData) {
51
            Event request = new Event();
52
            request.Data = requestData;
53
            request.Scope = scope.Concat(new Scope("/" + name));
54
            request.Method = "REQUEST";
55

  
56
            Event reply = this.Call(name, request);
57
            if (reply.Data.GetType().Equals(typeof(ReplyType)))
58
            {
59
                return (ReplyType)reply.Data;
60
            }
61
            else {
62
                throw new RSBException(String.Format("Unexpected Datatype! (Expected: {0} | Got: {1})", typeof(ReplyType).ToString(), reply.Data.GetType().ToString()));
63
            }   
64
        }
65

  
66
        public void Activate() {
67
            foreach (var remoteMethod in connections.Values) {
68
                remoteMethod.Activate();
69
            }
70
            IsActive = true;
71
        }
72

  
73
        public void Deactivate() {
74
            foreach (var remoteMethod in connections.Values) {
75
                remoteMethod.Deactivate();
76
            }
77
            IsActive = false;
78
        }
79
    }
80
}
rsb-cil/rsb-cil.csproj
52 52
    <Compile Include="Rsb\Converter\UInt32Converter.cs" />
53 53
    <Compile Include="Rsb\Converter\UInt64Converter.cs" />
54 54
    <Compile Include="Rsb\Factory.cs" />
55
    <Compile Include="Rsb\Patterns\DataCallback.cs" />
56
    <Compile Include="Rsb\Patterns\ICallback.cs" />
57
    <Compile Include="Rsb\Patterns\LocalServer.cs" />
58
    <Compile Include="Rsb\Patterns\Method.cs" />
59
    <Compile Include="Rsb\Patterns\RemoteMethod.cs" />
60
    <Compile Include="Rsb\Patterns\RemoteServer.cs" />
55 61
    <Compile Include="Rsb\Protocol\EventId.cs" />
56 62
    <Compile Include="Rsb\Protocol\EventMetaData.cs" />
57 63
    <Compile Include="Rsb\Protocol\Notification.cs" />
58
-