summaryrefslogtreecommitdiff
path: root/CommonAPI-Examples/e02Attributes/README
blob: 758e4fff0340ced577a7636603081443aebf1f94 (plain)
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
Example 2: Attributes
~~~~~~~~~~~~~~~~~~~~~~

Consider the Franca IDL specification of example 2:

[source,java]
----
package commonapi.examples

interface E02Attributes {
	version { major 1 minor 0 }

	attribute Int32 x

	attribute CommonTypes.a1Struct a1
}

typeCollection CommonTypes {

	struct a1Struct {
		String s
		a2Struct a2
	}

	struct a2Struct {
		Boolean b
		Double d
	}
}
----

Modelling attributes in interfaces means in general that the service that implements this interface has an internal state which shall be visible for external clients like a HMI (Human Machine Interface). A developer of a client would normally expect that he can set and get the attribute and that he can notify or subscribe to changes of the value of the attribute. We will see below in the implementation how exactly this is realized by CommonAPI. Franca offers two key words that indicate exactly how the attribute can be accessed: +readonly+ and +noSubscriptions+. The default setting is that everything is allowed; with these two additional key words these possibilies can be limited (eg if someone tries to call a set method and the attribute is readonly he will get an error at compile time).

The nested structure +a1Struct+ is defined in a type collection +CommonTypes+. Structures can be defined just like other type definitions within an interface definition or outside in a type collection. Since Franca 0.8.9 type collections can also be anonymous (without name). A type collection is transferred by the CommonAPI code generator in an additional namespace.

The Franca interface specification of attributes does not contain any information about whether the access from client side is synchronous or asynchronous or whether the attribute is cached by the proxy. CommonAPI provides always methods for synchronous and asynchronous setter and getter methods; caching can be realized via an API extension.

Now let's have a look to the CommonAPI code on the service side. The default implementation of the stub which is generated by the CommonAPI codegenerator defines the attribute as private attribute of the stub class. This attribute can be accessed from the stub implementation via getter and setter functions. Additionaly the API for the stub implementation provides some callbacks (the following code snippet shows parts of the generated stub class which refer to the attribute x):

[source,{cppstr}]
----
class E02AttributesStubDefault : public virtual E02AttributesStub {
 public:
	E02AttributesStubDefault();

	virtual const int32_t& getXAttribute(
		const std::shared_ptr<CommonAPI::ClientId> clientId);

	virtual void setXAttribute(
		const std::shared_ptr<CommonAPI::ClientId> clientId, int32_t value);

 protected:
	virtual bool trySetXAttribute(int32_t value);
	virtual bool validateXAttributeRequestedValue(const int32_t& value);
	virtual void onRemoteXAttributeChanged();

 private:
	int32_t xAttributeValue_;
};
----

If the implementation of the stub has to change the value of the attribute +x+, let's say in a class +E02AttributesStubImpl+ that is derived from +E02AttributesStubDefault+, then it can call +setXAttribute+ (analog the usage of +getXAttribute+). The callback +onRemoteXAttributeChanged+ informs that a change of the attribute +x+ has been completed. The other callbacks can prevent the set of the attribute (+validateXAttributeRequestedValue+) or change the given value from the client (+trySetXAttribute+).

In the example the service increments a counter every 2 seconds and publishes the counter value via the interface attribute +x+.

Now see the implementation of the client. The simplest case is to get the current value of +x+. The following extract shows one part of the main function:

[source,{cppstr}]
----
#include <iostream>

#include <CommonAPI/CommonAPI.h>
#include <commonapi/examples/E02AttributesProxy.h>

using namespace commonapi::examples;

int main() {

	std::shared_ptr < CommonAPI::Runtime > runtime = CommonAPI::Runtime::load();

	std::shared_ptr < CommonAPI::Factory > factory = runtime->createFactory();
	const std::string& serviceAddress = 
		"local:commonapi.examples.Attributes:commonapi.examples.Attributes";
	std::shared_ptr < E02AttributesProxyDefault > myProxy =
		factory->buildProxy < E02AttributesProxy > (serviceAddress);

    while (!myProxy->isAvailable()) { usleep(10); }

	CommonAPI::CallStatus callStatus;
	int32_t value = 0;

	// Get actual attribute value from service
	myProxy->getXAttribute().getValue(callStatus, value);
	if (callStatus != CommonAPI::CallStatus::SUCCESS) {
		std::cerr << "Remote call A failed!\n";
		return -1;
	}
	std::cout << "Got attribute value: " << value << std::endl;

}
----

The +getXAttribute+ method will deliver the type +XAttribute+ which has to be used for the access to +x+. Every access returns a flag named callStatus (please see the CommonAPI specification). Subscription requires in general the definition of a callback function which is called in case of an attribute change. The subscribe method of CommonAPI requires a function object; for a compact notation this function object can be defined as lambda function:

[source,{cppstr}]
----
myProxy->getXAttribute().getChangedEvent().subscribe([&](const int32_t& val) {
	std::cout << "Received change message: " << val << std::endl;
});
----

Of course it is also possible to define a separate callback function with an user-defined name (here recv_cb):

[source,{cppstr}]
----
void recv_cb(const CommonAPI::CallStatus& callStatus, const int32_t& val) {
	std::cout << "Receive callback: " << val << std::endl;
}

.... // main method

// Subscribe for receiving values, alternative implementation 1
std::function<void (int32_t)> f = recv_msg;
myProxy->getXAttribute().getChangedEvent().subscribe(f);

// Subscribe for receiving values, alternative implementation 2
myProxy->getXAttribute().getChangedEvent().subscribe(
	std::bind(recv_msg, std::placeholders::_1));
----

Asynchronous setting of attributes via setValueAsync works analog as shown in the following code extract where the more complex attribute _a1_ is set from the client:

[source,{cppstr}]
----
void recv_cb_s(const CommonAPI::CallStatus& callStatus,
		const CommonTypes::a1Struct& valStruct) {

	std::cout << "Receive callback for structure: a1.s = " <<
		valStruct.s << ", valStruct.a2.d = " << valStruct.a2.d << std::endl;
}
.... // main method

CommonTypes::a1Struct valueStruct;

valueStruct.s = "abc";
valueStruct.a2.b = true;
valueStruct.a2.d = 1234;

std::function<void (const CommonAPI::CallStatus&, CommonTypes::a1Struct)> fcb_s =
		recv_cb_s;

myProxy->getA1Attribute().setValueAsync(valueStruct, fcb_s);
----

As described above, in the chapter "Attribute Extensions" of this tutorial, it is possible to extend the standard CommonAPI for attributes by defining Attribute Extensions. In this example you have to:

* include the template definition of the extension (AttributeCacheExtension.hpp)
* to call a different factory method for creating the proxy (e.g. buildProxyWithDefaultAttributeExtension)
* and then it is possible to call the new defined methods, e.g.

[source,{cppstr}]
----
int32_t valueCached = 0;
bool r;
r = myProxy->getXAttributeExtension().getCachedValue(valueCached);
std::cout << "Got cached attribute value[" << (int)r << "]: " << valueCached << std::endl;
----

See the source code of the example for a deeper insight.