CDPConnector Class
The CDPConnector handles communication with a CDPComponent or CDPObject. More...
Header: | #include <CDPSystem/Base/CDPConnector.h> |
Inherits: | CDP::StudioAPI::CDPNode |
Public Functions
CDPConnector() | |
CDPConnector(const char *remoteName) | |
~CDPConnector() override | |
std::string | ClosestParentCDPObjectName() const |
virtual void | Configure(XMLElementEx *pEx) |
void | ConnectTo(const char *remoteName) |
bool | Connected() const |
virtual void | Create(const char *connectorName, CDPComponent *pOwnerComponent) |
std::string | GetProperty(const char *propertyName, bool &bSuccess) |
bool | GetPublishRoutingStatus() const |
CDPComponent * | OwnerComponent() const |
void | Preserve() |
bool | Preserved() const |
void | Release() |
unsigned int | RemoteHandle() const |
std::string | RemoteName() const |
std::string | RemoteShortName() const |
const char * | RemoteState() const |
bool | RemoteState(const char *stateToCheck) const |
unsigned int | RemoteStatus() const |
unsigned int | SendMessage(const MessagePacketHandle &message) |
unsigned int | SendMessage(Message *msg, bool retryEnabled = true) |
unsigned int | SendMessage(const char *command, unsigned int origin = 0) |
unsigned int | SendMessage(const char *command, const char *parameter, unsigned int origin = 0) |
unsigned int | SendMessage(unsigned int command, unsigned int origin = 0) |
void | SendMessageNoRetry(Message *msg) |
void | SetAckLocalMessages(bool ackLocal) |
void | SetConnectionChangeCallback(const std::function<void( bool connected ) > &callback) |
void | SetProperty(const char *propertyName, const char *propertyValue, int saveOnChange = -1) |
void | SetPublishRoutingStatus(bool publishRoutingStatus) |
virtual bool | SubscribeToEventNotifications(IEventReceiver *pReceiver, unsigned int eventFilter = 0xffffffff) |
virtual bool | UnsubscribeFromEventNotifications(IEventReceiver *pReceiver, unsigned int eventFilter = 0xffffffff) |
Reimplemented Public Functions
virtual void | FillNodeChildren(CDP::StudioAPI::NodeStream &serializer) const override |
virtual const std::string | GetNodeName() const override |
virtual std::string | GetNodeTypeName() const override |
- 26 public functions inherited from CDP::StudioAPI::CDPNode
- 22 public functions inherited from CDP::StudioAPI::ICDPNode
Additional Inherited Members
- 1 protected function inherited from CDP::StudioAPI::CDPNode
Detailed Description
The CDPConnector handles communication with a CDPComponent or CDPObject.
CDPConnector Features
- Creates a communication channel to a remote CDPObject or CDPComponent.
- Simplifies sending of messages to remote object
- Simplifies retrieval of information about the remote object
- The connector automatically connects when the remote object name is set.
- Automatically handles connection state; Disconnects if remote object goes away, connects when it comes back up.
Normal Usage
By adding a CDPConnector in CDP Studio Code mode or in Configure mode, code is automatically generated into your model so that the to CDPConnector is instantiated and created correctly. From there it is easy to use:
- Check if remote object is Connected()
- SendMessage() to the remote object
- Read RemoteStatus() and RemoteState() of the remote object/component
- GetProperty/SetProperty in the remote object
Example: By adding 'myConnector' to 'MyComponent':
CDPConnector myConnector; // autogenerated in MyComponent header file ... myConnector.Create("myConnector",this); // auto-generated in MyComponent::Create() method ... myConnector.ConnectTo("MyApp.MyCDPComponent.MyCDPObject"); // ConnectTo() should be called to tell which remote object the connector should connect to, can e.g. be done in Configure() or later. Not needed if the connector is already configured with correct remote name. ... void MyComponent::ProcessStart() { if (myConnector.Connected() && startWanted && !started) myConnector.SendMessage("Start"); }
The remote object name should normally be a CDPComponent or CDPObject, but the connector will also apparently connect to a CDPNode, but it has connected to the closest parent CDPComponent/CDPObject. See more information about this in ClosestParentCDPObjectName().
Connection Status and State
- Use Connected() to determine if the remote object is connected.
- Connected means the communication link is valid and that state and handle information is updated (see Periodic Updates below).
- Not Connected means communication link is not valid; i.e. the specified object is not available on the network.
The CDPConnector retrieves an object (alarm) Status through the call to RemoteStatus(). Component State can be requested/tested by calling RemoteState(), or by checking RemoteState(wantedState).
Example:
void MyComponent::ProcessNull() { if(myConnector.Connected() && myConnector.RemoteState("Online")) requestedState = "Online"; }
Sending Messages to Objects
The SendMessage() API is used to send messages. Connectors can send message content (limited to 996 bytes per message) to the connected object.
The simplest way to send a message, is to just send a text-command. Example:
if (pumpConnector.Connected()) pumpConnector.SendMessage("Start");
The above code assumes a pump connector is connected to a pump CDPComponent. It is also assumed that the pump CDPComponent has a Message handler (as added, for instance in Code mode; CDP Add Message ) that handles this "Start" message. When the pump CDPComponent receives the "Start" message, the MessageStart function is invoked.
To send additional information in the message:
if (pumpConnector.Connected() && pumpConnector.RemoteState("Stopped")) pumpConnector.SendMessage("Start","AdditionalInformation");
The MessageHandler can decode the message:
int MyComponent::MessageStart(void* message) { MessageTextCommandWithParameterReceive* msg = static_cast<MessageTextCommandWithParameterReceive*>(message); std::string strParameter(msg->parameters); CDPMessage("%s: Received Start Message with parameter %s\n",Name(),strParameter.c_str()); return 1; }
Message Acknowledgement
When the Retry parameter to SendMessage is non-zero, an acknowledge message (ACK / success) or not acknowledge (NACK / failure) is returned to the sender, dependant on success or failure to transmit a message.
The object that sends the Message can verify message reception or failure in either the MessageConfirm() (ACK) or the MessageTimeout() (NACK) functions. Example:
void MyComponent::ProcessNull() { static bool sent = false; if(pump.Connected()) { pump.SendMessage("Start"); sent = true; } } int MyComponent::MessageTimedOut(void* messageAck) { MessageACK* pMack = static_cast<MessageACK*>(messageAck); unsigned int handle = pMack->originalMessage.Destination(); CDPMessage("%s: Got Timeout for message to Handle : 0x%08x, sendStatus=%i\n",Name(),handle,pMack->sendStatus); return 1; } int MyComponent::MessageConfirm(void* messageAck) { MessageACK* pMack = static_cast<MessageACK*>(messageAck); unsigned int handle = pMack->originalMessage.Destination(); CDPMessage("%s: Got Confirmation for message to Handle : 0x%08x, sendStatus=%i\n",Name(),handle,pMack->sendStatus); return 1; }
The MessageAck message contains either the original message sent, or it could be a message in response to the message sent, depending on what the receiver implements. The message data that is passed in to the receiving component is also returned in the acknowledgement message. Since the Receiver can choose to modify this message, it is possible to implement a simple RPC mechanism using the SendMessage protocol.
Retrieving Properties
Properties can be requested from the connected object, even if it is on a different application. The GetProperty() call in CDPConnector will retrieve the property from the local property store if it can be found there. If it can not be found, a subscription will be set up to request the property (and future property changes) from the source object, and failure is returned to the caller. The CDP framework will propagate the property from the source object to the local store, so when GetProperty() is called some time in the future, the value will be present in the local store and returned together with the 'success' argument set to true.
It is also possible to get a callback when the property value arrives. The caller must then inherit the class from IEventReceiver and implement the pure virtual functions it provides. For all CDPObject based objects, a rudimentary (do-nothing) implementation has already been implemented. To get callbacks, the user must call CDPConnector::SubscribeToEventNotifications(this) and override RemotePropertyChanged() to handle reception of the property when that callback is called.
Note: Be aware that the RemotePropertyChanged() callback is not invoked if GetProperty() returns success (i.e if the property is found locally), even if a Subscription has been set up.
Example: Assuming, in your Component 'PumpController', you have a connector 'pump' and you want to know which Model it has:
void PumpController::ProcessNull() { if (pump.Connected() && !modelRequested) { pump.SubscribeToEventNotifications(this); // Set up subscription so RemotePropertyChanged is called when property is received bool success = false; std::string model = pump.GetProperty("Model",success); if(success) RemotePropertyChanged(pump.RemoteHandle(),"Model",model); // locally found property will not cause callback to be called, so we invoke it manually. modelRequested = true; } }
The IEventReceiver::RemotePropertyChanged() will be invoked when the property has been retrieved:
void PumpController::RemotePropertyChanged(unsigned int remoteHandle, std::string propertyName, std::string propertyValue) { if(remoteHandle==pump.RemoteHandle() && propertyName=="Model") { // Print out the model: CDPMessage("%s: pump has model %s\n", Name(), propertyValue.c_str()); // Tell the connector we are no longer interested in eventNotifications: RunInComponentThread([=]{pump.UnsubscribeFromEventNotifications(this);}); } }
Data Direction
When a connector is mainly used to send messages to its target and this needs to be indicated in block diagrams, the connector instance in a component should set two properties in the connector instance in the component model: Set the TypeHint="RoutingType" and RoutingType="Push" attributes inside the connector instance of the component model. To be able to set this the Hide Base Model Items filter must be disabled. The connector will now appear on the output side of the component in block diagram.
Periodic Updates
Status and state are retrieved periodically from remote components only ifRemoteState or RemoteStatus is requested. The very first time the RemoteState or RemoteStatus is read, the information will not be available, as this will be the signal to the update process that status and state information should be retrieved as well. RemoteStatus and RemoteState information are updated at the frequency (fs) of the Application, default is 100ms (for fs of 10Hz).
Member Function Documentation
CDPConnector::CDPConnector()
Constructs a CDPConnector
CDPConnector::CDPConnector(const char *remoteName)
Constructs a CDPConnector and connects to remoteName.
CDPConnector::~CDPConnector()
Releases connection and destroys the instance.
std::string CDPConnector::ClosestParentCDPObjectName() const
Returns closest parent CDPComponent/CDPObject of RemoteName()
If not Connected(), empty string is returned. If Connected(), RemoteName() will be returned if the full remote name does not contain any CDPNode(s) at the end of the name. If there are any CDPNode(s) at the end of the name, the closest parent CDPComponent/CDPObject name will be returned.
If e.g. the remote name is 'App.CDPComponent.CDPObject', 'App.CDPComponent.CDPObject' will be returned.
If e.g. the remote name is 'App.CDPComponent', 'App.CDPComponent' will be returned.
If e.g. the remote name is 'App.CDPComponent.CDPNode1.CDPNode2', 'App.CDPComponent' will be returned.
Note: If the remote name contains a CDPNode at the end of the name, the CDPConnector will send all messages to the closest parent CDPComponent/CDPObject, not to the CDPNode itself. If you try to connect to an object with a name you do not know is ending with a CDPNode or not, you should do this: When connected, compare RemoteName() with ClosestParentCDPObjectName(). If they are not equal, you may need to do some changes, depending on how you are using the connector. If you e.g. have done ConnectTo('App.CDPComponent.CDPNode'), ClosestParentCDPObjectName() returns 'App.CDPComponent', and you want to Set a property called 'myProperty' found in CDPNode, you should probably ConnectTo('App.CDPComponent') and change the propertyname to 'CDPNode.myProperty'.
See also CDPConnector::RemoteName().
[virtual]
void CDPConnector::Configure(XMLElementEx *pEx)
Runs Configure() for m_object and m_description. pEx must point to a xml Connector-line.
void CDPConnector::ConnectTo(const char *remoteName)
Connects to an object identified by remoteName, which is the full name of the object (local or remote) to connect to. Connection is not instant, so the user must call Connected() before attempting any operations on the connector.
bool CDPConnector::Connected() const
Gets connection status
Use Connected() to check if remote object is connected.
Note: Status/state is retrieved periodically from remote components only after RemoteStatus/RemoteState is requested. The very first time the RemoteState() or RemoteStatus() is read, the information will not be available, as this will be the signal to the update process that status and state information periodic updating should start.
Returns true
if communication link is valid and state and handle information is updated (see note). Returns false
if communication link is not valid; i.e. the specified object is not available on the network.
[virtual]
void CDPConnector::Create(const char *connectorName, CDPComponent *pOwnerComponent)
Initializes the instance. Creates CDPProperties and saves pOwnerComponent for later use.
[override virtual]
void CDPConnector::FillNodeChildren(CDP::StudioAPI::NodeStream &serializer) const
Reimplemented from CDPNode::FillNodeChildren().
Writes m_object
(routing to object), m_connected
and m_description
to serializer stream.
[override virtual]
const std::string CDPConnector::GetNodeName() const
Reimplemented from ICDPNode::GetNodeName().
Returns the local name given to the connector.
[override virtual]
std::string CDPConnector::GetNodeTypeName() const
Reimplemented from ICDPNode::GetNodeTypeName().
Returns a "CDPConnector"
.
std::string CDPConnector::GetProperty(const char *propertyName, bool &bSuccess)
Reads property from remote object.
If the property value is not available locally yet, the value will be requested, and (if SubscribeToEventNotifications has been called) then the owner component RemotePropertyChanged() will be called when the value arrives.
Callback-registration is done by calling SubscribeToEventNotifications( objectToReceiveNotify ) , and the callback will invoke the RemotePropertyChanged() function in objectToReceiveNotify when the property is retrieved.
Example code:
if(connector->Connected()) { connector->SubscribeToEventNotifications(this); bool success = false; std::string name = connector->GetProperty("Name",success); if(success) RemotePropertyChanged(connector->RemoteHandle(),"Name",name); }
bool CDPConnector::GetPublishRoutingStatus() const
Gets the Connectors Object property routing publish behavior in RoutingsTable.
CDPComponent *CDPConnector::OwnerComponent() const
Returns raw pointer to owner component.
void CDPConnector::Preserve()
Tells the underlying CDPConnection to keep the connection even if this CDPConnector is destroyed or released.
bool CDPConnector::Preserved() const
Gets the preserved state of this connection
void CDPConnector::Release()
Releases connection without deleting connector.
unsigned int CDPConnector::RemoteHandle() const
Gets handle of remote component.
std::string CDPConnector::RemoteName() const
Returns full remote object name.
See also CDPConnector::ClosestParentCDPObjectName().
std::string CDPConnector::RemoteShortName() const
Returns connected component name. If not connected then returns empty string.
const char *CDPConnector::RemoteState() const
Gets state of remote component.
bool CDPConnector::RemoteState(const char *stateToCheck) const
Returns true
if the component is in the specified state
unsigned int CDPConnector::RemoteStatus() const
Gets the most recently updated status of the remote object.
The status equals the or'ed statuses of the alarms and subcomponents. The Status bits are defined in CDPAlarm.
unsigned int CDPConnector::SendMessage(const MessagePacketHandle &message)
unsigned int CDPConnector::SendMessage(Message *msg, bool retryEnabled = true)
Sends custom message with or without retries.
This function is used to send messages already filled out by user code. Messages which is larger than the Message
struct may be sent. Also remember that all values in the Message must be on network format. Use macros MESSAGE_FILL and similar to help filling the message correctly.
Note: Set the parameterSize member correctly. This must be set to the number of bytes following after the parameterSize member.
msg - Pointer to message to send. retryEnabled - Set to true to make the messenger ask for acknowledge and resend the message if not aknowledge has been received.
See also Messenger and MESSAGE_FILL.
unsigned int CDPConnector::SendMessage(const char *command, unsigned int origin = 0)
Sends CM_TEXTCOMMAND message with specified text command (no parameters).
command - Text command to send origin - Origin handle to put in message to send. If 0
or not specified, handle of CDPConnector's owner or Messenger will be used.
unsigned int CDPConnector::SendMessage(const char *command, const char *parameter, unsigned int origin = 0)
Sends CM_TEXTCOMMAND message with specified text command and string parameter. Both command and parameter must be zero terminated strings.
command - Text command to send, must be zero terminated. parameter - Zero-terminated string parameter. origin - Origin handle to put in message to send. If 0
or not specified, handle of CDPConnector's owner or Messenger will be used.
unsigned int CDPConnector::SendMessage(unsigned int command, unsigned int origin = 0)
Sends binary message (without parameters).
command - Binary command to send, typically CM_ACTIVATE or similar. origin - Origin handle to put in message to send. If 0
or not specified, handle of CDPConnector's owner or Messenger will be used.
void CDPConnector::SendMessageNoRetry(Message *msg)
Sends message without retries (no acknowledgement requested from recipient). Same as SendMessage(Message*, false)
See also CDPConnector::SendMessage().
void CDPConnector::SetAckLocalMessages(bool ackLocal)
void CDPConnector::SetConnectionChangeCallback(const std::function<void( bool connected ) > &callback)
void CDPConnector::SetProperty(const char *propertyName, const char *propertyValue, int saveOnChange = -1)
Sends a message that sets destination object's CDPProperty named propertyName to propertyValue.
If an owner component is set, it will receive callbacks on message acks.
If optional argument saveOnChange is set to 1
, changed CDPProperty value is saved to XML. If 0
then value is changed but change is not saved to XML. Therefore it is lost after restarting the application. Default is -1
, then property's SaveOnChange setting is used to determine whether to save the value. Note that this argument will not update SaveOnChange setting, it just overrides it.
void CDPConnector::SetPublishRoutingStatus(bool publishRoutingStatus)
Sets the Connectors Object property routing publish behavior in RoutingsTable. By default CDPConnector publishes its routing in the CDP tables for tools to see.
[virtual]
bool CDPConnector::SubscribeToEventNotifications(IEventReceiver *pReceiver, unsigned int eventFilter = 0xffffffff)
Adds pReceiver to the list of objects that will receive updates when events from object with handle handleEventSource are received ( typically after a call to CDPConnector::GetProperty() )
The pReceiver will then receive a message describing the event. If handleEventSource==0xffffffff
, all events will be sent to pReceiver. Use eventFilter to mask out which events to subscribe to.
The eventFilter bits are defined like this:
#define EVENT_ALARMSET 0x00000001 // The alarm's Set flag/state was set. The alarm changed state to "Unack-Set" (The Unack flag was set if not already set) #define EVENT_ALARMCLEAR 0x00000002 // The alarm's Set flag was cleared. The Unack state is unchanged. #define EVENT_ALARMACK 0x00000004 // The alarm changed state from "Unacknowledged" to "Acknowledged". The Set state is unchanged. #define EVENT_PROPERTY_CHANGE 0x00000008 // Some property was changed. #define EVENT_REPRISE 0x00000040 // A repetition/update of an event that has been reported before. Courtesy of late subscribers. #define EVENT_SOURCE_OBJECT_UNAVAILABLE 0x00000100 // The provider of the event has become unavailable (disconnected or similar) #define EVENT_NODE_BOOT 0x40000000 // The provider reports that the CDPEventNode just have booted. #define EVENT_EXTENDED_INFO_AVAILABLE 0x80000000 // Indicates that this event also have sent extended event, and that the extended event info is available as extended event. #define EVENT_MASK_ALL 0xffffffff #define EVENT_MASK_ALARM_EVENT (EVENT_ALARMSET | EVENT_ALARMCLEAR | EVENT_ALARMACK)
See also UnsubscribeFromEventNotifications.
[virtual]
bool CDPConnector::UnsubscribeFromEventNotifications(IEventReceiver *pReceiver, unsigned int eventFilter = 0xffffffff)
Removes pReceiver from list of objects that will receive updates when events from object with handle handleEventSource are received.
The pReceiver will then receive a message describing the event. Use eventFilter to mask out which events to unsubscribe to.
See also SubscribeToEventNotifications.
Get started with CDP Studio today
Let us help you take your great ideas and turn them into the products your customer will love.