AgIsoStack++
A control-function-focused implementation of the major ISOBUS and J1939 protocols
Loading...
Searching...
No Matches
isobus_maintain_power_interface.cpp
5#include "isobus/utility/system_timing.hpp"
6
7#include <algorithm>
8#include <array>
9#include <cassert>
10
11namespace isobus
12{
13 MaintainPowerInterface::MaintainPowerInterface(std::shared_ptr<InternalControlFunction> sourceControlFunction) :
14 maintainPowerTransmitData(sourceControlFunction),
15 txFlags(static_cast<std::uint32_t>(TransmitFlags::NumberOfFlags), process_flags, this)
16 {
17 }
18
20 {
21 if (initialized)
22 {
23 CANNetworkManager::CANNetwork.remove_any_control_function_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::MaintainPower), process_rx_message, this);
24 CANNetworkManager::CANNetwork.remove_any_control_function_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::WheelBasedSpeedAndDistance), process_rx_message, this);
25 }
26 }
27
28 EventDispatcher<const std::shared_ptr<MaintainPowerInterface::MaintainPowerData>, bool> &MaintainPowerInterface::get_maintain_power_data_event_publisher()
29 {
31 }
32
37
39 {
40 if (initialized)
41 {
44 [](std::shared_ptr<MaintainPowerData> messageInfo) {
45 return SystemTiming::time_expired_ms(messageInfo->get_timestamp_ms(), MAINTAIN_POWER_TIMEOUT_MS);
46 }),
48 if (SystemTiming::time_expired_ms(maintainPowerTransmitTimestamp_ms, MAINTAIN_POWER_TIMEOUT_MS / 2) &&
50 (SystemTiming::get_time_elapsed_ms(keyOffTimestamp) < maintainPowerTime_ms) &&
52 {
53 txFlags.set_flag(static_cast<std::uint32_t>(TransmitFlags::SendMaintainPower));
54 maintainPowerTransmitTimestamp_ms = SystemTiming::get_timestamp_ms();
55 }
56 txFlags.process_all_flags();
57 }
58 else
59 {
60 LOG_ERROR("[Maintain Power]: Interface has not been initialized yet.");
61 }
62 }
63
64 MaintainPowerInterface::MaintainPowerData::MaintainPowerData(std::shared_ptr<ControlFunction> sendingControlFunction) :
65 sendingControlFunction(sendingControlFunction)
66 {
67 }
68
70 {
71 bool retVal = (inWorkState != currentImplementInWorkState);
72 currentImplementInWorkState = inWorkState;
73 return retVal;
74 }
75
80
82 {
83 bool retVal = (readyToWorkState != currentImplementReadyToWorkState);
84 currentImplementReadyToWorkState = readyToWorkState;
85 return retVal;
86 }
87
92
94 {
95 bool retVal = (parkState != currentImplementParkState);
96 currentImplementParkState = parkState;
97 return retVal;
98 }
99
104
106 {
107 bool retVal = (transportState != currentImplementTransportState);
108 currentImplementTransportState = transportState;
109 return retVal;
110 }
111
116
118 {
119 bool retVal = (currentMaintainActuatorPowerState != maintainState);
120 currentMaintainActuatorPowerState = maintainState;
121 return retVal;
122 }
123
128
130 {
131 bool retVal = (currentMaintainECUPowerState != maintainState);
132 currentMaintainECUPowerState = maintainState;
133 return retVal;
134 }
135
140
142 {
143 return sendingControlFunction;
144 }
145
147 {
148 timestamp_ms = timestamp;
149 }
150
152 {
153 return timestamp_ms;
154 }
155
157 {
158 if (!initialized)
159 {
160 CANNetworkManager::CANNetwork.add_any_control_function_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::MaintainPower), process_rx_message, this);
161 CANNetworkManager::CANNetwork.add_any_control_function_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::WheelBasedSpeedAndDistance), process_rx_message, this);
162 initialized = true;
163 }
164 }
165
167 {
168 return initialized;
169 }
170
171 void MaintainPowerInterface::set_maintain_power_time(std::uint32_t timeToMaintainPower)
172 {
173 maintainPowerTime_ms = timeToMaintainPower;
174 }
175
177 {
179 }
180
185
186 std::shared_ptr<MaintainPowerInterface::MaintainPowerData> MaintainPowerInterface::get_received_maintain_power(std::size_t index)
187 {
188 std::shared_ptr<MaintainPowerInterface::MaintainPowerData> retVal = nullptr;
189
190 if (index < receivedMaintainPowerMessages.size())
191 {
192 retVal = receivedMaintainPowerMessages.at(index);
193 }
194 return retVal;
195 }
196
198 {
199 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = {
200 static_cast<std::uint8_t>(0x0F | (static_cast<std::uint8_t>(maintainPowerTransmitData.get_maintain_actuator_power()) << 4) | (static_cast<std::uint8_t>(maintainPowerTransmitData.get_maintain_ecu_power()) << 6)),
201 static_cast<std::uint8_t>(static_cast<std::uint8_t>(maintainPowerTransmitData.get_implement_in_work_state()) |
202 static_cast<std::uint8_t>(static_cast<std::uint8_t>(maintainPowerTransmitData.get_implement_ready_to_work_state()) << 2) |
203 static_cast<std::uint8_t>(static_cast<std::uint8_t>(maintainPowerTransmitData.get_implement_park_state()) << 4) |
204 static_cast<std::uint8_t>(static_cast<std::uint8_t>(maintainPowerTransmitData.get_implement_transport_state()) << 6)),
205 0xFF,
206 0xFF,
207 0xFF,
208 0xFF,
209 0xFF,
210 0xFF
211 };
212
213 return CANNetworkManager::CANNetwork.send_can_message(static_cast<std::uint32_t>(CANLibParameterGroupNumber::MaintainPower),
214 buffer.data(),
215 buffer.size(),
216 std::static_pointer_cast<InternalControlFunction>(maintainPowerTransmitData.get_sender_control_function()));
217 }
218
219 void MaintainPowerInterface::process_flags(std::uint32_t flag, void *parentPointer)
220 {
221 if (static_cast<std::uint32_t>(TransmitFlags::SendMaintainPower) == flag)
222 {
223 assert(nullptr != parentPointer);
224 auto targetInterface = static_cast<MaintainPowerInterface *>(parentPointer);
225
226 if (!targetInterface->send_maintain_power())
227 {
228 targetInterface->txFlags.set_flag(static_cast<std::uint32_t>(TransmitFlags::SendMaintainPower));
229 }
230 }
231 }
232
233 void MaintainPowerInterface::process_rx_message(const CANMessage &message, void *parentPointer)
234 {
235 assert(nullptr != parentPointer);
236
237 auto targetInterface = static_cast<MaintainPowerInterface *>(parentPointer);
238
239 if (static_cast<std::uint32_t>(CANLibParameterGroupNumber::WheelBasedSpeedAndDistance) == message.get_identifier().get_parameter_group_number())
240 {
241 // This is checked in the speed interface as well, but we need to know about it for the key switch state.
242 if (CAN_DATA_LENGTH == message.get_data_length())
243 {
244 if (nullptr != message.get_source_control_function())
245 {
246 // We don't care who's sending this really, we just need to detect a transition from not-off to off.
247 const auto decodedKeySwitchState = static_cast<KeySwitchState>((message.get_uint8_at(7) >> 2) & 0x03);
248
249 switch (decodedKeySwitchState)
250 {
252 {
253 if (0 != targetInterface->keyNotOffTimestamp)
254 {
255 LOG_INFO("[Maintain Power]: The key switch state has transitioned from NOT OFF to OFF.");
256 targetInterface->keyNotOffTimestamp = 0;
257
258 // Send the maintain power message based on the key state transition
259 targetInterface->keyOffTimestamp = SystemTiming::get_timestamp_ms();
260 targetInterface->txFlags.set_flag(static_cast<std::uint32_t>(TransmitFlags::SendMaintainPower));
261 targetInterface->maintainPowerTransmitTimestamp_ms = SystemTiming::get_timestamp_ms();
262 targetInterface->keySwitchOffEventPublisher.invoke();
263 }
264 else if (0 == targetInterface->keyOffTimestamp)
265 {
266 LOG_INFO("[Maintain Power]: The key switch state is detected as OFF.");
267 targetInterface->keyOffTimestamp = SystemTiming::get_timestamp_ms();
268 }
269 }
270 break;
271
273 {
274 if (0 != targetInterface->keyOffTimestamp)
275 {
276 LOG_INFO("[Maintain Power]: The key switch state has transitioned from OFF to NOT OFF.");
277 targetInterface->keyOffTimestamp = 0;
278 targetInterface->keyNotOffTimestamp = SystemTiming::get_timestamp_ms();
279 }
280 else if (0 == targetInterface->keyNotOffTimestamp)
281 {
282 LOG_INFO("[Maintain Power]: The key switch state is detected as NOT OFF.");
283 targetInterface->keyNotOffTimestamp = SystemTiming::get_timestamp_ms();
284 }
285 targetInterface->maintainPowerTransmitTimestamp_ms = 0;
286 }
287 break;
288
289 case KeySwitchState::Error:
290 {
291 LOG_WARNING("[Maintain Power]: The key switch is in an error state.");
292 targetInterface->keyOffTimestamp = 0;
293 targetInterface->keyNotOffTimestamp = 0;
294 targetInterface->maintainPowerTransmitTimestamp_ms = 0;
295 }
296 break;
297
298 default:
299 {
300 // The "take no action" state, so we'll ignore it.
301 }
302 break;
303 }
304 }
305 }
306 else
307 {
308 LOG_WARNING("[Maintain Power]: Received malformed wheel based speed PGN. DLC must be 8.");
309 }
310 }
311 else if (static_cast<std::uint32_t>(CANLibParameterGroupNumber::MaintainPower) == message.get_identifier().get_parameter_group_number())
312 {
313 if (CAN_DATA_LENGTH == message.get_data_length())
314 {
315 if (nullptr != message.get_source_control_function())
316 {
317 auto result = std::find_if(targetInterface->receivedMaintainPowerMessages.cbegin(),
318 targetInterface->receivedMaintainPowerMessages.cend(),
319 [&message](const std::shared_ptr<MaintainPowerData> &receivedInfo) {
320 return (nullptr != receivedInfo) && (receivedInfo->get_sender_control_function() == message.get_source_control_function());
321 });
322
323 if (result == targetInterface->receivedMaintainPowerMessages.end())
324 {
325 // There is no existing message object from this control function, so create a new one
326 targetInterface->receivedMaintainPowerMessages.push_back(std::make_shared<MaintainPowerData>(message.get_source_control_function()));
327 result = targetInterface->receivedMaintainPowerMessages.end() - 1;
328 }
329
330 auto &mpMessage = *result;
331 bool changed = false;
332
333 changed |= mpMessage->set_maintain_actuator_power(static_cast<MaintainPowerData::MaintainActuatorPower>((message.get_uint8_at(0) >> 4) & 0x03));
334 changed |= mpMessage->set_maintain_ecu_power(static_cast<MaintainPowerData::MaintainECUPower>((message.get_uint8_at(0) >> 6) & 0x03));
335 changed |= mpMessage->set_implement_in_work_state(static_cast<MaintainPowerData::ImplementInWorkState>(message.get_uint8_at(1) & 0x03));
336 changed |= mpMessage->set_implement_ready_to_work_state(static_cast<MaintainPowerData::ImplementReadyToWorkState>((message.get_uint8_at(1) >> 2) & 0x03));
337 changed |= mpMessage->set_implement_park_state(static_cast<MaintainPowerData::ImplementParkState>((message.get_uint8_at(1) >> 4) & 0x03));
338 changed |= mpMessage->set_implement_transport_state(static_cast<MaintainPowerData::ImplementTransportState>((message.get_uint8_at(1) >> 6) & 0x03));
339 mpMessage->set_timestamp_ms(SystemTiming::get_timestamp_ms());
340
341 targetInterface->maintainPowerDataEventPublisher.call(mpMessage, changed);
342 }
343 }
344 else
345 {
346 LOG_WARNING("[Maintain Power]: Received malformed maintain power PGN. DLC must be 8.");
347 }
348 }
349 }
350} // namespace isobus
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::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.
void add_any_control_function_parameter_group_number_callback(std::uint32_t parameterGroupNumber, CANLibCallback callback, void *parent)
Registers a callback for ANY control function sending the associated PGN.
bool send_can_message(std::uint32_t parameterGroupNumber, const std::uint8_t *dataBuffer, std::uint32_t dataLength, std::shared_ptr< InternalControlFunction > sourceControlFunction, std::shared_ptr< ControlFunction > destinationControlFunction=nullptr, CANIdentifier::CANPriority priority=CANIdentifier::CANPriority::PriorityDefault6, TransmitCompleteCallback txCompleteCallback=nullptr, void *parentPointer=nullptr, DataChunkCallback frameChunkCallback=nullptr)
This is the main way to send a CAN message of any length.
void remove_any_control_function_parameter_group_number_callback(std::uint32_t parameterGroupNumber, CANLibCallback callback, void *parent)
This is how you remove a callback added with add_any_control_function_parameter_group_number_callback...
MaintainActuatorPower
Enumerates the different states that can be requested in the "Maintain Actuator Power" SPN.
ImplementTransportState get_implement_transport_state() const
Returns the reported implement transport state.
bool set_implement_transport_state(ImplementTransportState transportState)
Sets the reported implement transport state.
bool set_implement_in_work_state(ImplementInWorkState inWorkState)
Sets the reported implement in-work state.
bool set_implement_park_state(ImplementParkState parkState)
Sets the reported implement park state.
std::shared_ptr< ControlFunction > get_sender_control_function() const
Returns a pointer to the sender of the message. If an ICF is the sender, returns the ICF being used t...
std::uint32_t get_timestamp_ms() const
Returns the timestamp for when the message was received, in milliseconds.
MaintainECUPower get_maintain_ecu_power() const
Returns the reported maintain ECU power state.
bool set_implement_ready_to_work_state(ImplementReadyToWorkState readyToWorkState)
Sets the reported implement ready to work state.
bool set_maintain_ecu_power(MaintainECUPower maintainState)
Sets the reported maintain ECU power state.
ImplementParkState get_implement_park_state() const
Returns the reported implement park state.
ImplementReadyToWorkState
Signal that indicates that an implement is connected to a tractor or power unit and is ready for work...
ImplementInWorkState
Signal that indicates that an implement is connected to a tractor or power unit and is in work state.
MaintainECUPower
Enumerates the different states that can be requested in the "Maintain ECU Power" SPN.
ImplementInWorkState get_implement_in_work_state() const
Returns the reported implement in-work state.
MaintainActuatorPower get_maintain_actuator_power() const
Returns the reported maintain actuator power state.
bool set_maintain_actuator_power(MaintainActuatorPower maintainState)
Sets the reported maintain actuator power state.
ImplementReadyToWorkState get_implement_ready_to_work_state() const
Returns the reported implement ready to work state.
ImplementParkState
Indicates the state of an implement where it may be disconnected from a tractor or power unit.
ImplementTransportState
Indicates the transport state of an implement connected to a tractor or power unit.
void set_timestamp_ms(std::uint32_t timestamp)
Sets the timestamp for when the message was received or sent.
Manages sending and receiving the maintain power message (PGN 65095)
EventDispatcher< const std::shared_ptr< MaintainPowerData >, bool > maintainPowerDataEventPublisher
An event publisher for notifying when new maintain power messages are received.
std::uint32_t maintainPowerTime_ms
The amount of time to ask the TECU to maintain actuator/section power. Will be rounded up to the next...
std::uint32_t maintainPowerTransmitTimestamp_ms
Timestamp used to know when to transmit the maintain power message in milliseconds.
bool initialized
Stores if the interface has been initialized.
KeySwitchState
Enumerates the key switch states of the tractor or power unit.
@ NotOff
Key is not off (does not always mean that it's on!)
std::shared_ptr< MaintainPowerData > get_received_maintain_power(std::size_t index)
Returns the content of a received maintain power message based on the index of the sender....
MaintainPowerInterface(std::shared_ptr< InternalControlFunction > sourceControlFunction)
Constructor for a MaintainPowerInterface.
void set_maintain_power_time(std::uint32_t timeToMaintainPower)
Use this to tell the interface how long it should transmit the maintain power message after it detect...
TransmitFlags
Enumerates a set of flags to manage transmitting messages owned by this interface.
@ SendMaintainPower
A flag to manage sending the maintain power message.
bool send_maintain_power() const
Transmits the maintain power message.
ProcessingFlags txFlags
Tx flag for sending the maintain power message. Handles retries automatically.
EventDispatcher< const std::shared_ptr< MaintainPowerData >, bool > & get_maintain_power_data_event_publisher()
Returns an event dispatcher which you can use to get callbacks when new/updated maintain power messag...
static void process_rx_message(const CANMessage &message, void *parentPointer)
Processes a CAN message.
~MaintainPowerInterface()
Destructor for a MaintainPowerInterface.
std::uint32_t keyOffTimestamp
A timestamp to track when the key is off, used to calculate how many messages to send and when to sen...
EventDispatcher & get_key_switch_transition_off_event_publisher()
Returns an event dispatcher which you can use to get callbacks when the key switch transitions from t...
bool get_initialized() const
Returns if the interface has been initialized.
MaintainPowerData maintainPowerTransmitData
Use this to configure the transmission of the maintain power message.
std::size_t get_number_received_maintain_power_sources() const
Returns the number of unique senders of the maintain power message.
EventDispatcher keySwitchOffEventPublisher
An event publisher for notifying when the key switch transitions to the off state.
void update()
Call this cyclically to update the interface. Transmits messages if needed and processes timeouts for...
std::uint32_t get_maintain_power_time() const
Returns the amount of time that the interface will continue to send the maintain power message after ...
static constexpr std::uint32_t MAINTAIN_POWER_TIMEOUT_MS
The amount of time that power can be maintained per message, used as the timeout as well.
static void process_flags(std::uint32_t flag, void *parentPointer)
Processes one flag (which sends the associated message)
std::vector< std::shared_ptr< MaintainPowerData > > receivedMaintainPowerMessages
A list of all received maintain power messages.
void initialize()
Sets up the class and registers it to receive callbacks from the network manager for processing messa...
Defines an interface for sending and receiving the maintain power message (PGN 65095).
This namespace encompasses all of the ISO11783 stack's functionality to reduce global namespace pollu...
constexpr std::uint8_t CAN_DATA_LENGTH
The length of a classical CAN frame.