• Non ci sono risultati.

9.1 Implementor objects

N/A
N/A
Protected

Academic year: 2021

Condividi "9.1 Implementor objects"

Copied!
32
0
0

Testo completo

(1)

Chapter 9

ZigApi implementation

In this chapter, the implementation of application abstraction middleware will be described in its most important details. The chapter will focus on the Zig- Bee clusters implementation through the implementor objects technique, on threading issues, and on device type detection technique.

9.1 Implementor objects

As real ZigBee devices offers a set of ZigBee clusters, a Device software object offers a set of cluster interfaces, one for each cluster. The functionalities of a single cluster have to be offered by several Device-derived classes. For example, the functionalities of Thermostat cluster are offered by HeatingDevice as well as by Refrigerator. It is a best practise in software engineering to implement only once a functionality, even when it is offered by various classes. Implementors are software classes whose aim is to implement the functionality of a single Zig- Bee cluster, in terms of attributes and operations. There is one implementor class for each ZigBee cluster. Device-derived classes use a set of implemen- tor classes in order to export their ZigBee clusters. In figure 9.1, the example of a Device-derived class (HeatingDevice) is shown. HeatingDevice uses four implementor objects (BasicClusterImpl, IdentifyClusterImpl, ThermostatClus- terImpl and AlarmsClusterImpl ), one for each cluster it exports.

Pseudocode 9.1 shows how the HeatingDevice class can export four inter- faces using four implementor objects in a programming language that forbids

92

(2)

9.1. Implementor objects 93

Figure 9.1: Implementors class diagram

multiple inheritance, like C#. Simple wrapper methods called hook functions are used. An hook function does nothing except calling the correspondent func- tion of the implementor, that contains the actual implementation. In this way, HeatingDevice is able to offer its cluster interfaces without directly implemen- ting them.

9.1.1 Implementors and attribute databases

When the programmer makes an operation on the attribute database of a Device-derived object, for example with GetAttrVal method, the specified at- tribute name must be searched in the attribute databases of its implementors.

As a matter of fact, the actual attribute database of a Device-derived object is constituted by its individual database together with those of its implementors.

Figure 9.2 shows this architecture.

In order to implement this feature, every Device-derived class maintains

an implementor database. With AddImplementor method, a Device-derived

object registers a new implementor to its implementor database. In this way,

HeatingDevice inherits the ZigBee attributes of its implementors.

(3)

9.1. Implementor objects 94

Algorithm 9.1 Implementors code example public class HeatingDevice : Device,

IBasicCluster, IIdentifyCluster, IThermostatCluster, IAlarmsCluster {

private basicClusterImpl;

private identifyClusterImpl;

private thermostatClusterImpl;

private alarmsClusterImpl;

protected HeatingDevice(initializer) {

basicClusterImpl = new BasicClusterImpl(initializer, owner=this);

identifyClusterImpl = new IdentifyClusterImpl(initializer, owner=this);

alarmsClusterImpl = new AlarmsClusterImpl(initializer, owner=this);

thermostatClusterImpl = new ThermostatClusterImpl(initializer, owner=this, alarmsClusterImpl);

AddImplementor(basicClusterImpl);

AddImplementor(identifyClusterImpl);

AddImplementor(thermostatClusterImpl);

AddImplementor(alarmsClusterImpl);

}

// Hook functions:

void IIdentifyCluster.Identify(ushort identifyTime, int timeOut) { identifyClusterImpl.Identify(identifyTime, timeOut); }

void IThermostatCluster.SetpointRaiseLower(SetpointRaiseLowerMode mode, sbyte amount, int timeOut)

{ thermostatClusterImpl.SetpointRaiseLower(mode, amount, timeOut); } void IAlarmsCluster.ResetAlarm(string alarmName, int timeOut)

{ alarmsClusterImpl.ResetAlarm(alarmName, timeOut); } void IAlarmsCluster.ResetAllAlarms(int timeOut)

{ alarmsClusterImpl.ResetAllAlarms(timeOut); } void IAlarmsCluster.SetAlarmCallback(string alarmName,

AlarmDelegate alarmCallback)

{ alarmsClusterImpl.SetAlarmCallback(alarmName, alarmCallback); } }

(4)

9.1. Implementor objects 95

Figure 9.2: Attribute database implementation

9.1.2 BasicClusterImpl class

BasicClusterImpl is a class aimed to implement the ZigBee Basic cluster at- tributes and operations. It realizes IBasicCluster interface and the following constructor.

public BasicClusterImpl(Initializer initializer, ushort deviceProfileId,

Device ownerDevice);

Exceptions thrown:

ZigApi.GatewayCommunicationException

The initializer.Info parameter has an invalid format.

The initializer structure contains the initialization parameters of the Device

(5)

9.1. Implementor objects 96

object; deviceProfileId is the ZigBee profile identifier under which the cluster will work; ownerDevice is the owner of the implementor, or null if the imple- mentor has no owner.

9.1.3 OnOffClusterImpl class

OnOffClusterImpl is a class aimed to implement the ZigBee On/off cluster attributes and operations. It realizes IOnOffCluster interface and the following constructor.

public OnOffClusterImpl(Initializer initializer, ushort deviceProfileId,

Device ownerDevice);

Exceptions thrown:

ZigApi.GatewayCommunicationException

The initializer.Info parameter has an invalid format.

The initializer structure contains the initialization parameters of the Device object; deviceProfileId is the ZigBee profile identifier under which the cluster will work; ownerDevice is the owner of the implementor, or null if the imple- mentor has no owner.

9.1.4 IdentifyClusterImpl class

IdentifyClusterImpl is a class aimed to implement the ZigBee Identify cluster attributes and operations. It realizes IIdentifyCluster interface and the follo- wing constructor.

public IdentifyClusterImpl(Initializer initializer, ushort deviceProfileId,

Device ownerDevice);

(6)

9.1. Implementor objects 97

Exceptions thrown:

ZigApi.GatewayCommunicationException

The initializer.Info parameter has an invalid format.

The initializer structure contains the initialization parameters of the Device object; deviceProfileId is the ZigBee profile identifier under which the cluster will work; ownerDevice is the owner of the implementor, or null if the imple- mentor has no owner.

9.1.5 AlarmsClusterImpl class

AlarmsClusterImpl is a class aimed to implement the ZigBee Alarms cluster attributes and operations. It realizes IAlarmsCluster interface and the following methods.

public AlarmsClusterImpl(Initializer initializer, ushort deviceProfileId,

Device ownerDevice);

Exceptions thrown:

ZigApi.GatewayCommunicationException

The initializer.Info parameter has an invalid format.

The initializer structure contains the initialization parameters of the Device object; deviceProfileId is the ZigBee profile identifier under which the cluster will work; ownerDevice is the owner of the implementor, or null if the imple- mentor has no owner.

public void RegisterAlarm(string alarmName, ushort clusterId,

byte alarmId);

(7)

9.1. Implementor objects 98

Registers a new type of alarm.

Exceptions thrown:

System.InvalidOperationException

An alarm type with the same name has been already registered.

This function does not generate ZigBee traffic. It is used only to define new alarm types when defining custom devices (see section 9.6). Warning: this is not a thread-safe method, so the programmer must call it whithin constructor of Device-derived classes only. clusterId is the identifier of the cluster which signals this type of alarm. alarmId is the ZigBee alarm’s identifier (unique within its cluster).

9.1.6 ThermostatClusterImpl class

ThermostatClusterImpl a is class aimed to implement the ZigBee Thermostat cluster attributes and operations. It realizes IThermostatCluster interface and the following constructor.

public ThermostatClusterImpl(Initializer initializer, ushort deviceProfileId,

Device ownerDevice,

AlarmsClusterImpl alarmsClusterImpl);

Exceptions thrown:

ZigApi.GatewayCommunicationException

The initializer.Info parameter has an invalid format.

The initializer structure contains the initialization parameters of the Device

object; deviceProfileId is the ZigBee profile identifier under which the cluster

will work; ownerDevice is the owner of the implementor, or null if the im-

plementor has no owner. Parameter alarmsClusterImpl is a reference to the

Alarms cluster implementor, needed to register Thermostat cluster alarms (see

(8)

9.2. Device type detection 99

subsection 9.1.5). If null is assigned to this parameter, no alarms will be regis- tered.

9.1.7 BoxClusterImpl class

BoxClusterImpl a is class aimed to implement the ZigBee Box cluster attributes and operations. It realizes IBoxCluster interface and the following constructor.

public BoxClusterImpl(Initializer initializer, ushort deviceProfileId,

Device ownerDevice);

Exceptions thrown:

ZigApi.GatewayCommunicationException

The initializer.Info parameter has an invalid format.

The initializer structure contains the initialization parameters of the Device object; deviceProfileId is the ZigBee profile identifier under which the cluster will work; ownerDevice is the owner of the implementor, or null if the imple- mentor has no owner.

9.2 Device type detection

In order to detect the type of a physical device, the proper serviceDescriptor

gateway feature is used, which in turn uses a standard message of the ZigBee

network layer. This method cannot be used for virtual devices, because the

service descriptor of a device GAL is constant and predefined, regardless of

the virtual device application connected to it. In such a case, the device type

detection is performed by the applicative layer, retrieving the value of the Model

identifier attribute of the ZigBee Basic cluster, which must be implemented by

every virtual device. The algorithm in pseudocode 9.2 realizes the device type

detection.

(9)

9.2. Device type detection 100

Algorithm 9.2 Device type detection Device dev;

serviceDescr = AskServiceDescr(newDeviceInfo);

if (serviceDescr == RefrigeratorServiceDescr) { dev = new Refrigerator(newDeviceInfo);

}

else if (serviceDescr == ...) { ...

}

else if (serviceDescr == DeviceGalServiceInfo) { // Virtual device:

BasicClusterImpl dummyDevice = new BasicClusterImpl(newDeviceInfo);

modelId = dummyDevice.GetModelId();

if (modelId == RefrigeratorModelId) { dev = new Refrigerator(newDeviceInfo);

}

else if (modelId == ...) { ...

}

else <Error>;

}

else <Error>;

(10)

9.3. Synchronization 101

9.3 Synchronization

Multi-threading is one of the key characteristics of ZigApi. The ZigApi appli- cation interface is thread-safe, meaning that its methods can (and should) be called by different execution threads.

Multi-threading is very important in applications that manage graphical in- terfaces. In GUI applications, a single thread (the GUI thread ) is the owner of a window, and its responsibility is to dispatch the GUI events (mouse clicks, key- board keys’ pressing, ecc.) in a single main loop, and execute the instructions associated to each event (handler functions). If blocking methods, like those to operate on network and devices, are called in handler functions, the GUI’s main loop is also blocked, and the human user sees the window freezing. While frozen, a window does not refresh its content and it is dumb to user input.

Such behaviour tends to be annoying for the user and it is highly deprecated in some applications.

The use of a parallel thread to execute blocking functions is the preferred solution in most cases. With a multi-threading approach it is possible to:

• avoid graphical interface freezing;

• perform more blocking operations contemporaneously, for example opera- tions on single devices, increasing the performance of the application;

• receive asynchronous notifications (for example, alarms) from the network or from devices, without performing expensive and poorly reactive polling operations;

• perform periodic operations in background from the GUI thread.

Multi-threading approach brings synchronization and mutual exclusion issues that the programmer has to face. This section explains how ZigApi solves its own synchronization and gives the programmer the tools to solve higher level synchronization.

9.3.1 Network internal coherence maintenance

This synchronization requirement forbids symultaneous executions of incompa-

tible operations on the Network object. For example a gateway disconnection

(11)

9.3. Synchronization 102

and a contemporaneous operation on a device, or a reading of a device’s attri- bute and the contemporaneous leaving of such device from the network.

All the network’s and devices’ operations are classified in two categories.

• Fixed-network operations.

• Network-changing operations.

Fixed-network operations are those that do not change network characteristics, but require them not to change for all the entire duration of the operation. For example an operation of reading or writing on a device’s attribute, that re- quires the device not to leave the network in the meanwhile. Network-changing operations are those that (possibly) change the network characteristics. For example an inquiry operation, that can increase the set of devices connected to the network. The execution of an network-changing operation is not compatible with the contemporaneous execution of another similar operation, or of a fixed- network operation. On the contrary, the executions of two contemporaneous fixed-network operations are compatible. In this way, the programmer can or- der parallel operations on different devices or on the same device, increasing the bandwidth utilization, as shown in figures 9.3 and 9.4. The possibility of multiple operations on the same device at the same time permits the program- mer to perform complex parallelizable operations in an efficient way, especially in case of high round-trip delays.

Synchronization is solved with a classic readers-writers coordinator object, provided by the standard threading library CIL environment, ReaderWriter- LockSlim[10]. The network-changing operations ask the write-lock on the Net- work object, whereas the fixed-network operations ask the read-lock. Reader- WriterLockSLim natively permits lock recursion and implements anti-starvation policies.

In order to allow the programmer to implement complex operations, he/she must be able to operate directly on the network lock. For this reason, Network class offers explicit lock and unlock methods.

• Freeze and Unfreeze to implement complex fixed-network operations, like

iterations on connected Devices’ list.

(12)

9.3. Synchronization 103

Figure 9.3: Two contemporaneous transactions on different devices

Figure 9.4: Two contemporaneous transactions on the same device

(13)

9.4. Transactions management 104

• FreezeToChange and UnfreezeToChange to implement complex fixed-network operations that can possibly perform network-changing operation too.

• Lock and Unlock to implement complex network-changing operations.

See subsection 8.1.10 for detailed information.

9.4 Transactions management

The gateway abstraction layer offers two simple mechanisms to send and receive messages.

• sendMessage primitive, that sends an outcoming message to the specified (unicast of broadcast) destination.

• Asynchronous receive callback, a function that is executed by a dedicated thread when an incoming message arrives from the network.

ZigApi uses these simple techniques to offer more complex communication pri- mitives needed by ZigBee applicative protocol and based on the concept of transactions.

A transaction is defined as a set of messages between a client device and one or more server devices, that constitutes a single logical unit of applicative dialog. The client is always the ZigApi middleware and the servers are the devices. Transactions are divided in five general types.

1. Unicast transactions. They are transactions made up of two messages, a request message from the client to the server and a reply message back- ward. An example of this type of transactions are the couple of ZigBee messages WriteAttributes and WriteAttributesResponse.

2. Multicast transactions. They are transactions made up of one request broadcast or multicast message from the client, and several reply messages from the servers. An example of this transaction is the IdentifyQuery and IdentifyQueryResponse ZigBee messages.

3. Reply-less transactions. They are transactions made up of a single request

message from the client to the server. They can be considered unicast

(14)

9.4. Transactions management 105

transactions without the reply message. The programmer can perform some operations on devices in reply mode, or, if the application do not need such reply, in reply-less mode. Therefore, the same operation can be realized by a unicast transaction or by a reply-less transaction.

4. Unsolicited transactions. They are transactions made up of a single reply message from the server to the client. They can be considered unicast transactions without the request message. An example of this type of transactions is a ReportAttributes ZigBee message.

5. Complex transactions. They are transactions made up of two or more simple transactions of the previous types.

The object MessageCoordinator offers the synchronization primitives for unicast, multicast and unsolicited transactions. reply-less transactions are im- plemented by simple sendMessage gateway primitives, whereas complex tran- sactions are implemented by combining simple transactions with synchroniza- tion primitives.

9.4.1 MessageCoordinator object

The MessageCoordinator ’s services are based on the concept of message filters.

A message filter is a function that takes a message as an input parameter, and returns the boolean value true if such message has particular properties, usually on the format of the payload. The message filters are used by Messa- geCoordinator to isolate particular incoming messages that make part of some transaction.

Pseudocode 9.3, 9.4 and 9.5 shows respectively how a unicast transaction and a multicast transaction are implemented and how an unsolicited transac- tion is registered. Pseudocode 9.6 shows how to implement a complex transac- tion made up of two simple transactions. P() and V() respectively represent a wait and a signal operations on a synchronization semaphore. P LowPrio() represents a wait operation with a lower priority. When a V() is performed, a thread blocked on P LowPrio() is awakened only if no thread is blocked with P(). P LowPrio() is useful for threads implementing time-outs.

The MessageCoordinator interface offers the following operations.

(15)

9.4. Transactions management 106

Algorithm 9.3 Unicast transaction void unicastTransaction() {

network.Freeze();

TxMessage request;

int expectedSeqNo = device.PickNewTransSeqNo();

<Build request>;

Delegate replyFilter(RxMessage msg) { if(msg.seqNo == expectedSeqNo)

return true;

else

return false;

};

RxMessage reply = MessageCoordinator.UnicastTrans(

address, endPoint, request, replyFilter, 1500);

if(reply == null) { network.Unfreeze();

throw new TimeOutException();

}

<Parse reply>;

network.Unfreeze();

}

(16)

9.4. Transactions management 107

Algorithm 9.4 Multicast transaction void multicastTransaction() {

network.Freeze();

TxMessage request;

int expectedSeqNo = network.PickNewTransSeqNo();

<Build request>;

Delegate replyFilter(RxMessage msg) { if (msg.seqNo == expectedSeqNo)

return true;

else

return false;

};

MessageCoordinator.BeginMulticastTrans(

sourceList, request,

multicastAddress, replyFilter, timeOut);

RxMessage reply = MessageCoordinator.RecvMulticastTrans();

while(reply != null) {

<Parse reply>;

reply = MessageCoordinator.RecvMulticastTrans();

}

MessageCoordinator.EndMulticastTrans();

network.Unfreeze();

}

(17)

9.4. Transactions management 108

Algorithm 9.5 Unsolicited transaction Device.Constructor() {

...

Delegate unsolicitedMsgFilter(RxMessage msg) {

if(msg.commandId == CommandIdReportAttributes) return true;

else

return false;

};

MessageCoordinator.RegisterUnsolicitedTrans(

address, endPoint,

unsolicitedMsgFilter, ReportAttributeCallback);

}

void ReportAttributeCallback(RxMessage report) {

<Parse message>;

<Notify asynchronously the user>;

}

(18)

9.4. Transactions management 109

Algorithm 9.6 Complex transaction void complexTransaction() {

Semaphore endSimpleTr1 = 0;

Semaphore endSimpleTr2 = 0;

network.Freeze();

simpleTransaction1.StartThread(endSimpleTr1);

simpleTransaction2.StartThread(endSimpleTr2);

P(endSimpleTr1);

P(endSimpleTr2);

<Combine results>;

network.Unfreeze();

}

thread simpleTransaction1(Semaphore endSimpleTr1) {

<Perform simple transaction 1>

V(endSimpleTr1);

}

thread simpleTransaction2(Semaphore endSimpleTr2) {

<Perform simple transaction 2>

V(endSimpleTr2);

}

(19)

9.4. Transactions management 110

public void RxMessage UnicastTrans(

string serverLongAddr, byte serverEndPoint,

LastActionDlgt lastAction, MsgFilterDlgt replyFilter, int timeOut);

Performs a simple unicast transaction. This is a blocking function.

Exceptions thrown:

System.ArgumentException Negative or 0 time-out specified.

This method does the following actions.

1. Performs a last action specified by the programmer. Usually a message sending (request ).

2. Atomically with the last action, it starts listening for a reply message from the specified address/end point, that satisfies a particular format filter.

3. It blocks until such reply message is received, or until the time-out is exceeded. The time-out period starts from the lastAction execution end.

4. If the reply message is received within a time out, it’s returned.

5. Otherwise null is returned.

If the first two operations were not executed atomically, the reply message could arrive between them, causing the MessageCoordinator to discard it. The parameters of the function have the following meaning.

• serverLongAddr. The device’s 64-bit address from which receive the reply.

• serverEndPoint. The device’s end point from which receive the reply.

• lastAction. A function to execute before starting listening.

• msgFilter. The reply filter.

(20)

9.4. Transactions management 111

• timeOut. The time-out length of the transaction, in milliseconds.

public void BeginMulticastTrans(

ServerList serverList, LastActionDlgt lastAction, MsgFilterDlgt replyFilter, int timeOut);

Begins a multicast transaction. This is not a blocking function.

Exceptions thrown:

System.ArgumentException Negative or 0 time-out specified.

System.InvalidOperationException

The current thread is already owning another unfinished multicast transac- tion.

This method does the following actions.

1. Performs a last action specified by the programmer. Usually a message sending (request ).

2. Atomically with the last action, it starts listening for reply messages from the specified servers, that satisfies a particular format filter.

The parameters of the function have the following meaning.

• serverList. A list of couples (64-bit address, end point) from which receive the replies.

• lastAction. A function to execute before starting listening.

• msgFilter. The reply filter.

• timeOut. The time-out length of the transaction, in milliseconds.

The time-out period starts from the lastAction execution end. The thread

that called BeginMulticastTrans is called the owner thread of the multicast

(21)

9.4. Transactions management 112

transaction. It remains the owner until the transaction is terminated, with EndMulticastTrans. No thread but the owner can carry on and terminate a multicast transaction. A single thread can not own more than one multicast transaction at the same time.

public RxMessage RecvMulticastTrans();

Receive a reply of the owned multicast transaction. This is a blocking func- tion.

Exception thrown:

System.InvalidOperationException

The current thread does not own any multicast transaction.

This method does the following actions.

1. It blocks until the next reply message is received, or until the time-out is exceeded.

2. If a reply message is received within a time out, it’s returned.

3. Otherwise null is returned.

The programmer has to call this function on the same thread he called Begin- MulticastTrans. Once RecvMulticastTrans has returned null, following calls of the function will always return null, so they are useless.

public void EndMulticastTrans();

Terminates the owned multicast transaction. This is not a blocking func- tion.

Exception thrown:

System.InvalidOperationException

The current thread does not own any multicast transaction.

(22)

9.4. Transactions management 113

public void RegisterUnsolicitedTrans(

string serverLongAddr, byte serverEndPoint, MsgFilterDlgt msgFilter,

UnsolicitedTransCallbackDlgt callback);

Registers a new type of unsolicited transaction.

After it has been registered, every incoming message from the specified end point and matching the specified filter is considered an unsolicited transaction.

The parameters of the function have the following meaning.

• serverLongAddr. The device’s 64-bit address from which receive the mes- sages.

• serverEndPoint. The device’s end point from which receive the messages.

• msgFilter. The messages filter.

• callback. A function that MessageCoordinator will call everytime this type of unsolicited transaction arrives. This function si executed by a dedicated thread, so the programmer has to pay attention to threading issues.

public void MsgArrived(RxMessage msg);

Notifies the MessageCoordinator that a message has arrived. MessageCoor- dinator will apply message filters to identify the correct transaction that the message belongs to, and perform the relative actions. If no such tran- saction is found, the message is considered spurious and it is discarded.

The following pseudocode shows the implementation of MessageCoordinator class. A signal-and-return monitor paradigm[1] has been used, implemented by means of a set of binary synchronization semaphores. P() and V() respectively represent a wait and a signal operations on a synchronization semaphore.

public class MessageCoordinator {

(23)

9.4. Transactions management 114

private struct UnicastTransDescr { string serverAddr;

byte serverEndPoint;

MsgFilterDlgt replyFilter;

Semaphore cond = 0;

RxMessage reply;

};

private List<UnicastTransDescr> unicastTransDatabase;

public struct Server { string serverAddr;

byte serverEndPoint;

};

public typedef List<Server> ServerList;

private struct MulticastTransDescr { int ownerThreadId;

ServerList serverList;

MsgFilterDlgt replyFilter;

Semaphore cond = 0;

bool isWaiting;

bool isTimeOuted;

List<RxMessage> repliesBuffer;

};

private List<MulticastTransDescr> multicastTransDatabase;

struct UnsolicitedTransDescr { string serverAddr;

byte serverEndPoint;

MsgFilterDlgt msgFilter;

Delegate callback;

};

List<UnsolicitedTransDescr> unsolicitedTransDatabase;

Semaphore mutex = 1;

(24)

9.4. Transactions management 115

public RxMessage UnicastTrans(

string serverAddr, byte serverEndPoint,

LastActionDlgt lastAction, Delegate replyFilter, int timeOut)

{

P(mutex);

Semaphore cond = 0;

WaitDescriptor descr;

descr.serverAddr = serverAddr;

descr.serverEndPoint = serverEndPoint;

descr.replyFilter = replyFilter;

descr.cond = new Semaphore(0);

lastAction();

unicastTransDatabase.Add(descr);

thread timeOutTh { Sleep(timeOut);

P_LowPrio(mutex);

if (unicastTransDatabase.Contains(descr)) V(descr.cond); // <- mutex handing else

V(mutex);

}

timeOutTh.Start();

V(mutex);

P(descr.cond);

// -> mutex picking

if(descr.reply != null)

{

(25)

9.4. Transactions management 116

// A reply has arrived:

RxMessage ret = descr.reply;

unicastTransDatabase.Remove(descr);

V(mutex);

return ret;

} else {

// Time-out exceeded without reply:

V(mutex);

return null;

} }

public void BeginMultiTransaction(

ServerList serverList, LastActionDlgt lastAction, MsgFilterDlgt replyFilter, int timeOut)

{

P(mutex);

MulticastTransDescr descr;

descr.ownerThreadId = CurrentThreadId;

descr.serverList = severList;

descr.replyFilter = replyFilter;

descr.cond = new Semaphore(0);

descr.isWaiting = false;

descr.isTimeOuted = false;

lastAction();

multicastTransDatabase.Add(descr);

thread timeOutTh { Sleep(timeOut);

P_LowPrio(mutex);

if (!multicastTransDatabase.Contains(descr)) {

(26)

9.4. Transactions management 117

V(mutex);

return;

}

descr.isTimeOuted = true;

if (descr.isWaiting) { descr.isWaiting = false;

V(descr.cond); // <- mutex handing }

else

V(mutex);

}

timeOutTh.Start();

V(mutex);

}

public RxMessage RecvMultiTransaction() { P(mutex);

MulticastTransDescr descr = multicastTransDatabase.Search(

CurrentThreadId);

if(descr == null) { V(mutex);

throw System.InvalidOperationException();

}

if (descr.repliesBuffer.Empty()) { if (descr.isTimeOuted) {

V(mutex);

return null;

}

descr.isWaiting = true;

V(mutex);

P(descr.cond);

// -> mutex picking

if (descr.repliesBuffer.Empty()) {

(27)

9.4. Transactions management 118

// Time-out exceeded:

V(mutex);

return null;

} }

// A reply message is present:

RxMessage reply = descr.repliesBuffer.Pop();

V(mutex);

return reply;

}

public void EndMultiTransaction() { P(mutex);

MultiTransactionDescr descr = multicastTransDatabase.Search(

CurrentThreadId);

if(descr == null) { V(mutex);

throw System.InvalidOperationException();

}

multicastTransDatabase.Remove(descr);

V(mutex);

}

public void RegisterUnsolicitedTrans ( string serverAddr,

byte serverEndPoint, MsgFilterDlgt msgFilter,

UnsolicitedTransCallbackDlgt callback) {

P(mutex);

UnsolicitedTransDescr descr;

descr.serverAddr = serverAddr;

descr.serverEndPoint = serverEndPoint;

descr.msgFilter = msgFilter;

(28)

9.4. Transactions management 119

descr.callback = callback;

unsolicitedTransDatabase.Add(descr);

V(mutex);

}

public void MsgArrived(RxMessage msg) { P(mutex);

// Searching for a matching unicast transaction:

foreach (UnicastTransDescr descr in unicastTransDatabase) { if (descr.serverAddr == msg.sourceAddr

&& descr.serverEndPoint == msg.sourceEndPoint

&& descr.replyFilter(msg) == true) {

descr.reply = msg;

V(descr.cond); // <- mutex handing return;

} }

// I didn’t find any matching unicast transaction.

// Searching for a matching multicast transaction:

foreach (MulticastTransDescr in multicastTransDatabase) { if (descr.serverList.Contains(msg.sourceAddr,

msg.sourceEndPoint)

&& descr.replyFilter(msg) == true

&& descr.isTimeOuted == false) {

// An active multi-transaction filter has matched // the message:

descr.repliesBuffer.Push(msg);

if (descr.isWaiting) { descr.isWaiting = false;

V(descr.cond); // <- mutex handing }

else

(29)

9.5. Transaction sequence numbers management 120

V(mutex);

return;

} }

// I didn’t find any matching multicast transaction.

// Searching for a matching unsolicited transaction:

foreach (UnsolicitedTransDescr descr in unsolicitedTransDatabase) { if (descr.serverAddr == msg.sourceAddr

&& descr.serverEndPoint == msg.sourceEndPoint

&& descr.msgFilter(msg) == true) {

V(mutex);

descr.callback(msg);

return;

} }

// Spurious message. I discard it:

V(mutex);

}

}; // class MessageCoordinator

9.5 Transaction sequence numbers management

Transaction sequence number is an 8-bit field of the standard ZigBee applica- tion header. ZigBee application protocol imposes that all the messages inclu- ded in the same transaction must share the same transaction sequence number.

Successive transactions must use progressive sequence numbers. The successive sequence number of 0xFF is 0x00 (wrap-around).

The TransSeqNoProducer class implements a generator of transaction se-

quence numbers. Every time a transaction is performed, it asks a fresh se-

quence number to a TransSeqNoProducer object. Since symultaneous tran-

(30)

9.6. External extensibility 121

sactions can be performed by different threads, TransSeqNoProducer methods are thread-safe. Each Device object maintains a TransSeqNoProducer for uni- cast and reply-less transactions, while the Network object maintains a global TransSeqNoProducer for multicast transactions. Having multiple generators of sequence numbers minimizes the number of wrap-arounds. If a device’s gene- rator and the network’s generator generate the same sequence number, the transactions are still discriminated by means of the different message types.

9.6 External extensibility

The implementor objects allow to have a single implementation of a reusable cluster, independently from the number of devices using it. In this way, ZigApi is easily extensible.

Besides, ZigApi is externally extensible. This means that the programmer can define new clusters and new device types from outside ZigApi assembly, eventually at runtime. The programmer-defined clusters and devices are called custom clusters and custom device types. Custom devices can stand on the same network with the internally-defined devices. Pseudocode 9.7 shows an example of custom cluster definition with its implementor class.

Algorithm 9.7 Custom cluster definition interface IMyCluster {

void MyOperation(int timeOut);

}

class MyClusterImpl : Device, IMyCluster { public const ushort MyClusterId = 0xFCFC;

public MyClusterImpl(network, profileId, ownerDevice) { ...

AddAttrDescr("MyAttribute", int, 0x0000, MyClusterId);

}

public void MyOperation(int timeOut) {

< ... actual implementation ... >

} }

(31)

9.6. External extensibility 122

AddAttrDescr registers a new ZigBee attribute to the attribute database, named “MyAttribute” and typed as a 32-bit signed integer. Its ZigBee attribute identifier is 0x0000 within the cluster MyClusterId. Pseudocode 9.8 shows an example of custom device using the custom cluster previously defined and the standard Basic cluster.

Algorithm 9.8 Custom device type definition

class MyDevice : Device, IMyCluster, IBasicCluster { private readonly MyClusterImpl myClusterImpl;

private readonly BasicClusterImpl basicClusterImpl;

public MyDevice(network) {

myClusterImpl = new MyClusterImpl(network, HomeAutomationId, this);

basicClusterImpl = new BasicClusterImpl(network, HomeAutomationId, this);

AddImplementor(myClusterImpl);

AddImplementor(basicClusterImpl);

}

// Hook functions:

public void MyOperation(timeOut) { myClusterImpl.MyOperation(timeOut);

} }

Pseudocode 9.9 shows how to register the newly defined device to ZigApi.

RegisterCustomDevice must be called when the gateway connection is off. On successive connections, every detected physical device with HomeAutomation as profile identifier and MyDeviceId as device identifier, and every virtual device with “VirtualMyDevice” as model identifier, is considered a MyDevice.

Pseudocode 9.10 shows how the programmer can check whether a device is

a custom device, know what kind of custom device, and eventually perform an

operation on it.

(32)

9.6. External extensibility 123

Algorithm 9.9 Custom device type registration ...

Network theNetwork = new Network("127.0.0.1:9000");

theNetwork.RegisterCustomDevice(

customDeviceName="MyDevice", profile=HomeAutomation, deviceId=MyDeviceId,

virtualDeviceModelId="VirtualMyDevice", MyDevice);

...

theNetwork.Connect(6000);

...

Algorithm 9.10 Custom device type test

Device dev = theNetwork.DeviceAt(address);

if (dev.Type == CustomDevice && dev.CustomTypeName == "MyDevice") { ((MyDevice)dev).MyOperation(5000);

}

Riferimenti

Documenti correlati

Since the aim of this research is to prove the effectiveness of abrasive recycling with an economical approach, the final result will be the economics of the recycled GMA

The genetic diagnosis on polar globules does not result without limits, indeed, is not possible to determine the formation of any mosaicism (cells taken from the same

The resulting binary images are input into the spline-based algorithm for diameter estimates and the measurements performance against REVIEW is reported in Table 8.. The removal

Uno dei momenti del procedimento impositivo lasciato alla discrezionalità dell’Ente è certamente quello attinente alla riscossione: la potestà regolamentare degli enti

The epilogue by Muñoz-Morcillo that wraps up the book in direct continuity with his introductory chapter demonstrates that as editor of this volume he is more than a reviewer

The temperatures shown here are: local equilibrium tem- perature T , thermodynamic non-equilibrium temperature T neq (equal to the kinetic temperature along the x axis), the

So it is not only the technical nature of tasks and the existing technologies that determine the possibilities for automation, but the organisation of work.. Digital technologies

It can be seen that patients in phase ON tended to under- estimate the time interval that had to be measured, with respect to controls; the same patients, however,