Re: [Bacnet-developers] Timing Considerations and Message Handling Architecture
Brought to you by:
skarg
|
From: Steve K. <st...@ka...> - 2013-10-22 22:31:58
|
Hi Christoph,
> Concerning this architecture I would like to ask the following questions:
> 1) What was the reason for designing the BACNet stack with a callback mechanism
> for handling responses? E.g. why wasn't the npdu_handler(...) designed to 'just' decode
> the BACNet protocol and then update some receive message buffers, which can
> be simply read directly after npdu_handler(...) had been called successfully?
NPDU handling could be done in a lot of ways depending on the type of
device (i.e. a router would likely be different than a simple sensor).
The apdu_handler(), in the early versions of code, would call service
handlers directly (see bacnet-stack/ports/pic18f6720/apdu.c file), but
it was easier to use the stack by itself if the developers application
could simply register callbacks for each service handler.
When I first tried porting to C++ (via wxWidgets) many years ago,
though, the apdu.c failed since it had those callbacks. There is
probably a better way to handle the callback services (which, if
confirmed, should default to a reject message if not used) in C++,
such as virtual functions that can be overridden by the developer.
> 2) Are there any real-time constraints for the client, server and/or both sides of the
> communication?
There are some application layer time constraints in the tsm.c module
(a simplistic implementation of the transaction state machine
described in 135-2012 clause 5. THE APPLICATION LAYER), and some time
dependencies in the DeviceCommunicationControl service handling.
For BACnet/IP, though, there aren't really any real-time constraints.
> 3) Are there any other not-considered BACNet-related properties which might
> render the intended higher-level architecture 'useless'?
I'm not sure what that means.
> To better understand the architecture here is a fictive sample of a user-application:
> //Init: Sets up all handlers, data link, address cache etc.
> bacnetStackBase::BACNetConnection connection(...)
>
> //Let the connection run asynchronously as a thread.
> connection.run()
>
> //Begin the main routine
> //E.g. Here a user-application can handle RPCs from another system, GUI, etc.
> //NB: The keepRunning flag is a variable, which can be set to false via the
> // appropriate CTRL-C, etc handlers.
> while( keepRunning )
> {
> //Issue a read property request
> connection.send( ReadPropertyRequest( ... ), ... )
>
> //Check the length of the currently enqueued active responses.
> if( connection.activeResponses() )
> {
> //If the invoke IDs match and the response type is the same as
> //the request type the ReadPropertyRequest(...) has been answered.
> //NB: In case of an error an appropriate state flag will be set in the response.
Note: TSM timeout should also set an appropriate flag.
> ...
> }
>
> //Now here the user-application can interact with a GUI, etc.
> }
>
> //After the above loop has been quit the application should be gracefully
> //closed.
> connection.close()
>
> ---------------------------------- END OF PSEUDO-CODE
Seems okay, except that even the client must incorporate a BACnet
Device object and support ReadProperty service queries. So the loop
could look like:
while (keepRunning ) {
current_seconds = time(NULL);
pdu_len = datalink_receive(&src, &rx_buffer[0], MAX_MPDU, timeout);
if(pdu_len) {
myhandler_npdu(&src, &rx_buffer[0], pdu_len);
}
elapsed_seconds = current_seconds - last_seconds;
if (elapsed_seconds) {
/* at least one second has passed */
last_seconds = current_seconds;
dcc_timer_seconds(elapsed_seconds);
#if defined(BACDL_BIP) && BBMD_ENABLED
bvlc_maintenance_timer(elapsed_seconds);
#endif
dlenv_maintenance_timer(elapsed_seconds);
elapsed_milliseconds = elapsed_seconds * 1000;
EnterCriticalSection(&my_lock);
tsm_timer_milliseconds(elapsed_milliseconds);
LeaveCriticalSection(&my_lock);
}
}
And the ReadProperty request would be sent from another thread:
EnterCriticalSection(&my_lock);
invokeID = Send_Read_Property_Request_Address(&dest, MAX_APDU,
object_type, object_instance, property_id, BACNET_ARRAY_ALL);
LeaveCriticalSection(&my_lock);
Best Regards,
Steve
|