AgIsoStack++
A control-function-focused implementation of the major ISOBUS and J1939 protocols
Loading...
Searching...
No Matches
can_address_claim_state_machine.cpp
Go to the documentation of this file.
1//================================================================================================
8//================================================================================================
13#include "isobus/utility/system_timing.hpp"
14
15#include <cassert>
16#include <limits>
17#include <random>
18
19namespace isobus
20{
21 AddressClaimStateMachine::AddressClaimStateMachine(std::uint8_t preferredAddressValue, NAME ControlFunctionNAME, std::uint8_t portIndex) :
22 m_isoname(ControlFunctionNAME),
23 m_portIndex(portIndex),
24 m_preferredAddress(preferredAddressValue)
25 {
27 assert(portIndex < CAN_PORT_MAXIMUM);
28
30 {
31 // If we don't have a preferred address, your NAME must be arbitrary address capable!
33 }
34 std::default_random_engine generator;
35 std::uniform_int_distribution<unsigned int> distribution(0, 255);
36 m_randomClaimDelay_ms = distribution(generator) * 0.6f; // Defined by ISO part 5
37 CANNetworkManager::CANNetwork.add_global_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ParameterGroupNumberRequest), process_rx_message, this);
38 CANNetworkManager::CANNetwork.add_global_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::AddressClaim), process_rx_message, this);
39 }
40
41 AddressClaimStateMachine ::~AddressClaimStateMachine()
42 {
43 CANNetworkManager::CANNetwork.remove_global_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ParameterGroupNumberRequest), process_rx_message, this);
44 CANNetworkManager::CANNetwork.remove_global_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::AddressClaim), process_rx_message, this);
45 }
46
51
53 {
55 {
56 LOG_WARNING("[AC]: Address violation for address %u",
58
60 }
61 }
62
63 void AddressClaimStateMachine::process_commanded_address(std::uint8_t commandedAddress)
64 {
66 {
68 {
69 LOG_ERROR("[AC]: Our address was commanded to a new value, but our ISO NAME doesn't support changing our address.");
70 }
71 else
72 {
73 std::shared_ptr<ControlFunction> deviceAtOurPreferredAddress = CANNetworkManager::CANNetwork.get_control_function(m_portIndex, commandedAddress, {});
74 m_preferredAddress = commandedAddress;
75
76 if (nullptr == deviceAtOurPreferredAddress)
77 {
78 // Commanded address is free. We'll claim it.
80 LOG_INFO("[AC]: Our address was commanded to a new value of %u", commandedAddress);
81 }
82 else if (deviceAtOurPreferredAddress->get_NAME().get_full_name() < m_isoname.get_full_name())
83 {
84 // We can steal the address of the device at our commanded address and force it to move
86 LOG_INFO("[AC]: Our address was commanded to a new value of %u, and an ECU at the target address is being evicted.", commandedAddress);
87 }
88 else
89 {
90 LOG_ERROR("[AC]: Our address was commanded to a new value of %u, but we cannot move to the target address.", commandedAddress);
91 }
92 }
93 }
94 }
95
97 {
98 m_enabled = value;
99 }
100
102 {
103 return m_enabled;
104 }
105
107 {
108 return m_claimedAddress;
109 }
110
112 {
113 if (get_enabled())
114 {
115 switch (get_current_state())
116 {
117 case State::None:
118 {
120 }
121 break;
122
124 {
125 if (0 == m_timestamp_ms)
126 {
127 m_timestamp_ms = SystemTiming::get_timestamp_ms();
128 }
129 if (SystemTiming::time_expired_ms(m_timestamp_ms, m_randomClaimDelay_ms))
130 {
132 }
133 }
134 break;
135
137 {
139 {
141 }
142 }
143 break;
144
146 {
147 const std::uint32_t addressContentionTime_ms = 250;
148
149 if (SystemTiming::time_expired_ms(m_timestamp_ms, addressContentionTime_ms + m_randomClaimDelay_ms))
150 {
151 std::shared_ptr<ControlFunction> deviceAtOurPreferredAddress;
152
154 {
156 }
157
158 // Time to find a free address
159 if ((nullptr == deviceAtOurPreferredAddress) &&
161 {
162 // Our address is free. This is the best outcome. Claim it.
164 }
166 (deviceAtOurPreferredAddress->get_NAME().get_full_name() > m_isoname.get_full_name()))
167 {
168 // Our address is not free, we cannot be at an arbitrary address, and address is contendable
170 }
172 {
173 // Can't claim because we cannot tolerate an arbitrary address, and the CF at that spot wins contention
175 }
176 else
177 {
178 // We will move to another address if whoever is in our spot has a lower NAME
179 if ((nullptr == deviceAtOurPreferredAddress) ||
180 (deviceAtOurPreferredAddress->get_NAME().get_full_name() < m_isoname.get_full_name()))
181 {
183 }
184 else
185 {
187 }
188 }
189 }
190 }
191 break;
192
194 {
196 {
197 LOG_DEBUG("[AC]: Internal control function %016llx has claimed address %u on channel %u",
202 }
203 else
204 {
206 }
207 }
208 break;
209
211 {
212 // Search the range of generally available addresses
213 bool addressFound = false;
214
215 for (std::uint8_t i = 128; i <= 235; i++)
216 {
217 if ((nullptr == CANNetworkManager::CANNetwork.get_control_function(m_portIndex, i, {})) && (send_address_claim(i)))
218 {
219 addressFound = true;
220
222 {
224 }
225 LOG_DEBUG("[AC]: Internal control function %016llx could not use the preferred address, but has claimed address %u on channel %u",
227 i,
230 break;
231 }
232 }
233
234 if (!addressFound)
235 {
236 LOG_CRITICAL("[AC]: Internal control function %016llx failed to claim an address on channel %u",
240 }
241 }
242 break;
243
245 {
247 {
249 }
250 }
251 break;
252
254 {
256 }
257 break;
258
259 default:
260 {
261 }
262 break;
263 }
264 }
265 else
266 {
268 }
269 }
270
271 void AddressClaimStateMachine::process_rx_message(const CANMessage &message, void *parentPointer)
272 {
273 if (nullptr != parentPointer)
274 {
275 AddressClaimStateMachine *parent = reinterpret_cast<AddressClaimStateMachine *>(parentPointer);
276
277 if ((message.get_can_port_index() == parent->m_portIndex) &&
278 (parent->get_enabled()))
279 {
280 switch (message.get_identifier().get_parameter_group_number())
281 {
282 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::ParameterGroupNumberRequest):
283 {
284 const auto &messageData = message.get_data();
285 std::uint32_t requestedPGN = messageData.at(0);
286 requestedPGN |= (static_cast<std::uint32_t>(messageData.at(1)) << 8);
287 requestedPGN |= (static_cast<std::uint32_t>(messageData.at(2)) << 16);
288
289 if ((static_cast<std::uint32_t>(CANLibParameterGroupNumber::AddressClaim) == requestedPGN) &&
291 {
293 }
294 }
295 break;
296
297 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::AddressClaim):
298 {
299 if (parent->m_claimedAddress == message.get_identifier().get_source_address())
300 {
301 const auto &messageData = message.get_data();
302 std::uint64_t NAMEClaimed = messageData.at(0);
303 NAMEClaimed |= (static_cast<uint64_t>(messageData.at(1)) << 8);
304 NAMEClaimed |= (static_cast<uint64_t>(messageData.at(2)) << 16);
305 NAMEClaimed |= (static_cast<uint64_t>(messageData.at(3)) << 24);
306 NAMEClaimed |= (static_cast<uint64_t>(messageData.at(4)) << 32);
307 NAMEClaimed |= (static_cast<uint64_t>(messageData.at(5)) << 40);
308 NAMEClaimed |= (static_cast<uint64_t>(messageData.at(6)) << 48);
309 NAMEClaimed |= (static_cast<uint64_t>(messageData.at(7)) << 56);
310
311 // Check to see if another ECU is hijacking our address
312 // This is not really a needed check, as we can be pretty sure that our address
313 // has been stolen if we're running this logic. But, you never know, someone could be
314 // spoofing us I guess, or we could be getting an echo? CAN Bridge from another channel?
315 // Seemed safest to just confirm.
316 if (NAMEClaimed != parent->m_isoname.get_full_name())
317 {
318 // Wait for things to shake out a bit, then claim a new address.
321 LOG_WARNING("[AC]: Internal control function %016llx on channel %u must re-arbitrate its address because it was stolen by another ECU with NAME %016llx.",
322 parent->m_isoname.get_full_name(),
323 parent->m_portIndex,
324 NAMEClaimed);
325 }
326 }
327 }
328 break;
329
330 default:
331 {
332 }
333 break;
334 }
335 }
336 }
337 }
338
343
345 {
346 bool retVal = false;
347
348 if (get_enabled())
349 {
350 const std::uint8_t addressClaimRequestLength = 3;
351 const auto PGN = static_cast<std::uint32_t>(CANLibParameterGroupNumber::AddressClaim);
352 std::uint8_t dataBuffer[addressClaimRequestLength];
353
354 dataBuffer[0] = (PGN & std::numeric_limits<std::uint8_t>::max());
355 dataBuffer[1] = ((PGN >> 8) & std::numeric_limits<std::uint8_t>::max());
356 dataBuffer[2] = ((PGN >> 16) & std::numeric_limits<std::uint8_t>::max());
357
361 static_cast<std::uint32_t>(CANLibParameterGroupNumber::ParameterGroupNumberRequest),
362 static_cast<std::uint8_t>(CANIdentifier::CANPriority::PriorityDefault6),
363 dataBuffer,
364 3,
365 {});
366 }
367 return retVal;
368 }
369
371 {
372 bool retVal = false;
373
374 assert(address < BROADCAST_CAN_ADDRESS);
375
376 if (get_enabled())
377 {
378 std::uint64_t isoNAME = m_isoname.get_full_name();
379 std::uint8_t dataBuffer[CAN_DATA_LENGTH];
380
381 dataBuffer[0] = static_cast<uint8_t>(isoNAME);
382 dataBuffer[1] = static_cast<uint8_t>(isoNAME >> 8);
383 dataBuffer[2] = static_cast<uint8_t>(isoNAME >> 16);
384 dataBuffer[3] = static_cast<uint8_t>(isoNAME >> 24);
385 dataBuffer[4] = static_cast<uint8_t>(isoNAME >> 32);
386 dataBuffer[5] = static_cast<uint8_t>(isoNAME >> 40);
387 dataBuffer[6] = static_cast<uint8_t>(isoNAME >> 48);
388 dataBuffer[7] = static_cast<uint8_t>(isoNAME >> 56);
390 address,
392 static_cast<std::uint32_t>(CANLibParameterGroupNumber::AddressClaim),
393 static_cast<std::uint8_t>(CANIdentifier::CANPriority::PriorityDefault6),
394 dataBuffer,
396 {});
397 if (retVal)
398 {
399 m_claimedAddress = address;
400 }
401 }
402 return retVal;
403 }
404
405} // namespace isobus
Defines a class for managing the address claiming process.
Defines some PGNs that are used in the library or are very common.
The main class that manages the ISOBUS stack including: callbacks, Name to Address management,...
A class that acts as a logging sink. The intent is that someone could make their own derived class of...
std::uint8_t m_randomClaimDelay_ms
The random delay as required by the ISO11783 standard.
bool send_address_claim(std::uint8_t address)
Sends the address claim message.
void process_commanded_address(std::uint8_t commandedAddress)
Attempts to process a commanded address.
void update()
Updates the state machine, should be called periodically.
void set_current_state(State value)
Sets the current state machine state.
std::uint32_t m_timestamp_ms
A generic timestamp in milliseconds used to find timeouts.
std::uint8_t m_preferredAddress
The address we'd prefer to claim as (we may not get it)
std::uint8_t get_claimed_address() const
Returns the address claimed by the state machine or 0xFE if none claimed.
State get_current_state() const
Returns the current state of the state machine.
std::uint8_t m_portIndex
The CAN channel index to claim on.
std::uint8_t m_claimedAddress
The actual address we ended up claiming.
bool send_request_to_claim() const
Sends the PGN request for the address claim PGN.
State
Defines the state machine states for address claiming.
@ WaitForRequestContentionPeriod
State machine is waiting for the address claim contention period.
@ SendRequestForClaim
State machine is sending the request for address claim.
@ UnableToClaim
State machine could not claim an address.
@ SendReclaimAddressOnRequest
An ECU requested address claim, inform the bus of our current address.
@ None
Address claiming is uninitialized.
@ SendPreferredAddressClaim
State machine is claiming the preferred address.
@ WaitForClaim
State machine is waiting for the random delay time.
@ ContendForPreferredAddress
State machine is contending the preferred address.
@ SendArbitraryAddressClaim
State machine is claiming an address.
@ AddressClaimingComplete
Address claiming is complete and we have an address.
void on_address_violation()
Used to inform the address claim state machine that two CFs are using the same source address....
bool m_enabled
Enable/disable state for this state machine.
AddressClaimStateMachine(std::uint8_t preferredAddressValue, NAME ControlFunctionNAME, std::uint8_t portIndex)
The constructor of the state machine class.
void set_is_enabled(bool value)
Enables or disables the address claimer.
State m_currentState
The address claim state machine state.
static void process_rx_message(const CANMessage &message, void *parentPointer)
Processes a CAN message.
bool get_enabled() const
Returns if the address claimer is enabled.
@ PriorityDefault6
The default priority.
std::uint8_t get_source_address() const
Returns the source address of the frame encoded in the identifier.
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::uint8_t get_can_port_index() const
Returns the CAN channel index associated with the message.
const std::vector< std::uint8_t > & get_data() const
Gets a reference to the data in the CAN message.
CANIdentifier get_identifier() const
Returns the identifier of the message.
void add_global_parameter_group_number_callback(std::uint32_t parameterGroupNumber, CANLibCallback callback, void *parent)
This is how you register a callback for any PGN destined for the global address (0xFF)
bool send_can_message_raw(std::uint32_t portIndex, std::uint8_t sourceAddress, std::uint8_t destAddress, std::uint32_t parameterGroupNumber, std::uint8_t priority, const void *data, std::uint32_t size, CANLibBadge< AddressClaimStateMachine >) const
Sends a CAN message using raw addresses. Used only by the stack.
std::shared_ptr< ControlFunction > get_control_function(std::uint8_t channelIndex, std::uint8_t address, CANLibBadge< AddressClaimStateMachine >) const
Called only by the stack, returns a control function based on certain port and address.
static CANNetworkManager CANNetwork
Static singleton of the one network manager. Use this to access stack functionality.
void remove_global_parameter_group_number_callback(std::uint32_t parameterGroupNumber, CANLibCallback callback, void *parent)
This is how you remove a callback for any PGN destined for the global address (0xFF)
A class that represents an ISO11783 control function NAME from an address claim.
Definition can_NAME.hpp:24
bool get_arbitrary_address_capable() const
Returns if the ECU is capable of address arbitration.
Definition can_NAME.cpp:24
std::uint64_t get_full_name() const
Gets the raw 64 bit NAME.
Definition can_NAME.cpp:151
This namespace encompasses all of the ISO11783 stack's functionality to reduce global namespace pollu...
constexpr std::uint8_t NULL_CAN_ADDRESS
The NULL CAN address defined by J1939 and ISO11783.
constexpr std::uint8_t CAN_DATA_LENGTH
The length of a classical CAN frame.
constexpr std::uint8_t BROADCAST_CAN_ADDRESS
The global/broadcast CAN address.
constexpr std::uint32_t CAN_PORT_MAXIMUM
An arbitrary limit for memory consumption.