AgIsoStack++
A control-function-focused implementation of the major ISOBUS and J1939 protocols
Loading...
Searching...
No Matches
isobus_heartbeat.cpp
Go to the documentation of this file.
1//================================================================================================
16//================================================================================================
18
22#include "isobus/utility/system_timing.hpp"
23
24namespace isobus
25{
27 sendCANFrameCallback(sendCANFrameCallback)
28 {
29 }
30
32 {
33 if ((!enable) && (enable != enabled))
34 {
35 LOG_DEBUG("[HB]: Disabling ISOBUS heartbeat interface.");
36 }
37 enabled = enable;
38 }
39
41 {
42 return enabled;
43 }
44
45 bool HeartbeatInterface::request_heartbeat(std::shared_ptr<InternalControlFunction> sourceControlFunction,
46 std::shared_ptr<ControlFunction> destinationControlFunction) const
47 {
48 bool retVal = false;
49
50 if ((nullptr != sourceControlFunction) &&
51 (nullptr != destinationControlFunction) &&
52 enabled)
53 {
54 retVal = ParameterGroupNumberRequestProtocol::request_repetition_rate(static_cast<std::uint32_t>(CANLibParameterGroupNumber::HeartbeatMessage),
56 sourceControlFunction,
57 destinationControlFunction);
58 }
59 return retVal;
60 }
61
62 void HeartbeatInterface::on_new_internal_control_function(std::shared_ptr<InternalControlFunction> newControlFunction)
63 {
64 auto pgnRequestProtocol = newControlFunction->get_pgn_request_protocol().lock();
65
66 if (nullptr != pgnRequestProtocol)
67 {
68 pgnRequestProtocol->register_request_for_repetition_rate_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::HeartbeatMessage), process_request_for_heartbeat, this);
69 }
70 }
71
72 void HeartbeatInterface::on_destroyed_internal_control_function(std::shared_ptr<InternalControlFunction> destroyedControlFunction)
73 {
74 auto pgnRequestProtocol = destroyedControlFunction->get_pgn_request_protocol().lock();
75
76 if (nullptr != pgnRequestProtocol)
77 {
78 pgnRequestProtocol->remove_request_for_repetition_rate_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::HeartbeatMessage), process_request_for_heartbeat, this);
79 }
80 }
81
82 EventDispatcher<HeartbeatInterface::HeartBeatError, std::shared_ptr<ControlFunction>> &HeartbeatInterface::get_heartbeat_error_event_dispatcher()
83 {
85 }
86
87 EventDispatcher<std::shared_ptr<ControlFunction>> &HeartbeatInterface::get_new_tracked_heartbeat_event_dispatcher()
88 {
90 }
91
93 {
94 if (enabled)
95 {
96 trackedHeartbeats.erase(std::remove_if(trackedHeartbeats.begin(), trackedHeartbeats.end(), [this](Heartbeat &heartbeat) {
97 bool retVal = false;
98
99 if (nullptr != heartbeat.controlFunction)
100 {
101 if (ControlFunction::Type::Internal == heartbeat.controlFunction->get_type())
102 {
103 if ((SystemTiming::time_expired_ms(heartbeat.timestamp_ms, heartbeat.repetitionRate_ms)) &&
104 heartbeat.send(*this))
105 {
106 heartbeat.sequenceCounter++;
107
108 if (heartbeat.sequenceCounter > 250)
109 {
110 heartbeat.sequenceCounter = 0;
111 }
112 }
113 }
114 else if (SystemTiming::time_expired_ms(heartbeat.timestamp_ms, SEQUENCE_TIMEOUT_MS))
115 {
116 retVal = true; // External heartbeat is timed-out
117 LOG_ERROR("[HB]: Heartbeat from control function at address 0x%02X timed out.", heartbeat.controlFunction->get_address());
118 heartbeatErrorEventDispatcher.call(HeartBeatError::TimedOut, heartbeat.controlFunction);
119 }
120 }
121 else
122 {
123 retVal = true; // Invalid state
124 }
125 return retVal;
126 }),
127 trackedHeartbeats.end());
128 }
129 }
130
131 HeartbeatInterface::Heartbeat::Heartbeat(std::shared_ptr<ControlFunction> sendingControlFunction) :
132 controlFunction(sendingControlFunction),
133 timestamp_ms(SystemTiming::get_timestamp_ms())
134 {
135 }
136
138 {
139 bool retVal = false;
140 const std::array<std::uint8_t, 1> buffer = { sequenceCounter };
141
142 retVal = parent.sendCANFrameCallback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::HeartbeatMessage),
143 CANDataSpan(buffer.data(), buffer.size()),
145 nullptr,
147 if (retVal)
148 {
149 timestamp_ms = SystemTiming::get_timestamp_ms(); // Sent OK
150 }
151 return retVal;
152 }
153
155 {
156 if (enabled &&
157 (static_cast<std::uint32_t>(CANLibParameterGroupNumber::HeartbeatMessage) == message.get_identifier().get_parameter_group_number()) &&
158 (nullptr != message.get_source_control_function()) &&
159 (message.get_data_length() >= 1))
160 {
161 auto managedHeartbeat = std::find_if(trackedHeartbeats.begin(),
162 trackedHeartbeats.end(),
163 [&message](const Heartbeat &hb) {
164 return (message.get_source_control_function() == hb.controlFunction);
165 });
166
167 if (managedHeartbeat != trackedHeartbeats.end())
168 {
169 managedHeartbeat->timestamp_ms = SystemTiming::get_timestamp_ms();
170
171 if (message.get_uint8_at(0) == managedHeartbeat->sequenceCounter)
172 {
173 LOG_ERROR("[HB]: Duplicate sequence counter received in heartbeat.");
175 }
176 else if (message.get_uint8_at(0) != ((managedHeartbeat->sequenceCounter + 1) % 250))
177 {
178 LOG_ERROR("[HB]: Invalid sequence counter received in heartbeat.");
180 }
181 trackedHeartbeats.back().sequenceCounter = message.get_uint8_at(0);
182 }
183 else
184 {
185 LOG_DEBUG("[HB]: Tracking new heartbeat from control function at address 0x%02X.", message.get_source_control_function()->get_address());
186
187 if (message.get_uint8_at(0) != static_cast<std::uint8_t>(HeartbeatInterface::SequenceCounterSpecialValue::Initial))
188 {
189 LOG_WARNING("[HB]: Initial heartbeat sequence counter not received from control function at address 0x%02X.", message.get_source_control_function()->get_address());
190 }
191
192 trackedHeartbeats.emplace_back(message.get_source_control_function());
193 trackedHeartbeats.back().timestamp_ms = SystemTiming::get_timestamp_ms();
194 trackedHeartbeats.back().sequenceCounter = message.get_uint8_at(0);
196 }
197 }
198 }
199
200 bool HeartbeatInterface::process_request_for_heartbeat(std::uint32_t parameterGroupNumber,
201 std::shared_ptr<ControlFunction> requestingControlFunction,
202 std::shared_ptr<ControlFunction> targetControlFunction,
203 std::uint32_t repetitionRate,
204 void *parentPointer)
205 {
206 bool retVal = false;
207
208 if (nullptr != parentPointer)
209 {
210 auto interface = static_cast<HeartbeatInterface *>(parentPointer);
211
212 if ((interface->is_enabled()) &&
213 (static_cast<std::uint32_t>(CANLibParameterGroupNumber::HeartbeatMessage) == parameterGroupNumber))
214 {
215 retVal = true;
216
217 if (SEQUENCE_REPETITION_RATE_MS != repetitionRate)
218 {
219 LOG_WARNING("[HB]: Control function at address 0x%02X requested the ISOBUS heartbeat at non-compliant interval. Interval should be 100ms.", requestingControlFunction->get_address());
220 }
221 else
222 {
223 LOG_DEBUG("[HB]: Control function at address 0x%02X requested the ISOBUS heartbeat from control function at address 0x%02X.", requestingControlFunction->get_address(), targetControlFunction->get_address());
224 }
225
226 auto managedHeartbeat = std::find_if(interface->trackedHeartbeats.begin(),
227 interface->trackedHeartbeats.end(),
228 [targetControlFunction](const Heartbeat &hb) {
229 return (targetControlFunction == hb.controlFunction);
230 });
231
232 if (managedHeartbeat == interface->trackedHeartbeats.end())
233 {
234 interface->trackedHeartbeats.emplace_back(targetControlFunction); // Heartbeat will be sent on next update
235 }
236 }
237 }
238 return retVal;
239 }
240} // namespace isobus
Defines some PGNs that are used in the library or are very common.
A protocol that handles PGN requests.
A class that acts as a logging sink. The intent is that someone could make their own derived class of...
@ Priority3
Priority highest - 3 (Control messages priority)
std::uint32_t get_parameter_group_number() const
Returns the PGN encoded in the identifier.
A class that represents a generic CAN message of arbitrary length.
std::uint32_t get_data_length() const
Returns the length of the data in the CAN message.
std::shared_ptr< ControlFunction > get_source_control_function() const
Gets the source control function that the message is from.
std::uint8_t get_uint8_at(const std::uint32_t index) const
Get a 8-bit unsigned byte from the buffer at a specific index. A 8-bit unsigned byte can hold a value...
CANIdentifier get_identifier() const
Returns the identifier of the message.
static CANNetworkManager CANNetwork
Static singleton of the one network manager. Use this to access stack functionality.
std::shared_ptr< InternalControlFunction > get_internal_control_function(std::shared_ptr< ControlFunction > controlFunction)
Returns an internal control function if the passed-in control function is an internal type.
This class is used to store information about a tracked heartbeat.
bool send(const HeartbeatInterface &parent)
Transmits a heartbeat message (for internal control functions only). Updates the sequence counter and...
This class is used to send and receive ISOBUS heartbeats.
EventDispatcher< std::shared_ptr< ControlFunction > > newTrackedHeartbeatEventDispatcher
Event dispatcher for when a heartbeat message from another control function becomes tracked by this i...
static bool process_request_for_heartbeat(std::uint32_t parameterGroupNumber, std::shared_ptr< ControlFunction > requestingControlFunction, std::shared_ptr< ControlFunction > targetControlFunction, std::uint32_t repetitionRate, void *parentPointer)
Processes a PGN request for a heartbeat.
void set_enabled(bool enable)
This can be used to disable or enable this heartbeat functionality. It's probably best to leave it en...
EventDispatcher< std::shared_ptr< ControlFunction > > & get_new_tracked_heartbeat_event_dispatcher()
Returns an event dispatcher which can be used to register for new tracked heartbeat events....
std::list< Heartbeat > trackedHeartbeats
Store tracked heartbeat data, per CF.
@ InvalidSequenceCounter
The sequence counter is not valid.
EventDispatcher< HeartBeatError, std::shared_ptr< ControlFunction > > & get_heartbeat_error_event_dispatcher()
Returns an event dispatcher which can be used to register for heartbeat errors. Heartbeat errors are ...
bool is_enabled() const
Returns if the interface is currently enabled or not.
@ Initial
The heartbeat sequence number value shall be set to 251 once upon initialization of a CF.
bool enabled
Attribute that specifies if this interface is enabled. When false, the interface does nothing.
bool request_heartbeat(std::shared_ptr< InternalControlFunction > sourceControlFunction, std::shared_ptr< ControlFunction > destinationControlFunction) const
This method can be used to request that another control function on the bus start sending the heartbe...
void update()
Updates the interface. Called by the network manager, so there is no need for you to call it in your ...
void process_rx_message(const CANMessage &message)
Processes a CAN message, called by the network manager.
static constexpr std::uint32_t SEQUENCE_REPETITION_RATE_MS
A consuming CF shall send a Request for Repetition rate for the heart beat message with a repetition ...
EventDispatcher< HeartBeatError, std::shared_ptr< ControlFunction > > heartbeatErrorEventDispatcher
Event dispatcher for heartbeat errors.
const CANMessageFrameCallback sendCANFrameCallback
A callback for sending a CAN frame.
void on_destroyed_internal_control_function(std::shared_ptr< InternalControlFunction > destroyedControlFunction)
Called when an internal control function is deleted. Cleans up stale registrations with PGN request p...
void on_new_internal_control_function(std::shared_ptr< InternalControlFunction > newControlFunction)
Called by the internal control function class when a new internal control function is added....
HeartbeatInterface(const CANMessageFrameCallback &sendCANFrameCallback)
Constructor for a HeartbeatInterface.
static bool request_repetition_rate(std::uint32_t pgn, std::uint16_t repetitionRate_ms, std::shared_ptr< InternalControlFunction > source, std::shared_ptr< ControlFunction > destination)
Sends a PGN request for repetition rate.
Defines an interface for sending and receiving ISOBUS heartbeats. The heartbeat message is used to de...
This namespace encompasses all of the ISO11783 stack's functionality to reduce global namespace pollu...
DataSpan< const std::uint8_t > CANDataSpan
A read-only span of data for a CAN message.
std::function< bool(std::uint32_t parameterGroupNumber, CANDataSpan data, std::shared_ptr< InternalControlFunction > sourceControlFunction, std::shared_ptr< ControlFunction > destinationControlFunction, CANIdentifier::CANPriority priority)> CANMessageFrameCallback
A callback for communicating CAN message frames.