AgIsoStack++
A control-function-focused implementation of the major ISOBUS and J1939 protocols
Loading...
Searching...
No Matches
isobus_virtual_terminal_client_state_tracker.cpp
Go to the documentation of this file.
1//================================================================================================
8//================================================================================================
10
15
16#include <algorithm>
17
18namespace isobus
19{
21 client(client)
22 {
23 }
24
29
31 {
32 CANNetworkManager::CANNetwork.add_any_control_function_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::VirtualTerminalToECU), process_rx_or_tx_message, this);
33 CANNetworkManager::CANNetwork.add_any_control_function_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ECUtoVirtualTerminal), process_rx_or_tx_message, this);
34 CANNetworkManager::CANNetwork.add_global_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::VirtualTerminalToECU), process_rx_or_tx_message, this);
35 }
36
38 {
39 CANNetworkManager::CANNetwork.remove_any_control_function_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::VirtualTerminalToECU), process_rx_or_tx_message, this);
40 CANNetworkManager::CANNetwork.add_any_control_function_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ECUtoVirtualTerminal), process_rx_or_tx_message, this);
41 CANNetworkManager::CANNetwork.remove_global_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::VirtualTerminalToECU), process_rx_or_tx_message, this);
42 }
43
44 void VirtualTerminalClientStateTracker::add_tracked_numeric_value(std::uint16_t objectId, std::uint32_t initialValue)
45 {
46 if (numericValueStates.find(objectId) != numericValueStates.end())
47 {
48 LOG_WARNING("[VTStateHelper] add_tracked_numeric_value: objectId '%lu' already tracked", objectId);
49 return;
50 }
51
52 numericValueStates[objectId] = initialValue;
53 }
54
56 {
57 if (numericValueStates.find(objectId) == numericValueStates.end())
58 {
59 LOG_WARNING("[VTStateHelper] remove_tracked_numeric_value: objectId '%lu' was not tracked", objectId);
60 return;
61 }
62
63 numericValueStates.erase(objectId);
64 }
65
66 std::uint32_t VirtualTerminalClientStateTracker::get_numeric_value(std::uint16_t objectId) const
67 {
68 if (numericValueStates.find(objectId) == numericValueStates.end())
69 {
70 LOG_WARNING("[VTStateHelper] get_numeric_value: objectId '%lu' not tracked", objectId);
71 return 0;
72 }
73
74 return numericValueStates.at(objectId);
75 }
76
81
82 const std::deque<std::uint16_t> &VirtualTerminalClientStateTracker::get_mask_history() const
83 {
85 }
86
91
96
97 void VirtualTerminalClientStateTracker::add_tracked_soft_key_mask(std::uint16_t dataOrAlarmMaskId, std::uint16_t initialSoftKeyMaskId)
98 {
99 if (softKeyMasks.find(dataOrAlarmMaskId) != softKeyMasks.end())
100 {
101 LOG_WARNING("[VTStateHelper] add_tracked_soft_key_mask: data/alarm mask '%lu' already tracked", dataOrAlarmMaskId);
102 return;
103 }
104
105 softKeyMasks[dataOrAlarmMaskId] = initialSoftKeyMaskId;
106 }
107
109 {
110 if (softKeyMasks.find(dataOrAlarmMaskId) == softKeyMasks.end())
111 {
112 LOG_WARNING("[VTStateHelper] remove_tracked_soft_key_mask: data/alarm mask '%lu' was not tracked", dataOrAlarmMaskId);
113 return;
114 }
115
116 softKeyMasks.erase(dataOrAlarmMaskId);
117 }
118
120 {
122 {
123 LOG_WARNING("[VTStateHelper] get_active_soft_key_mask: the currently active data/alarm mask '%lu' is not tracked", activeDataOrAlarmMask);
124 return NULL_OBJECT_ID;
125 }
126
128 }
129
130 std::uint16_t VirtualTerminalClientStateTracker::get_soft_key_mask(std::uint16_t dataOrAlarmMaskId) const
131 {
132 if (softKeyMasks.find(dataOrAlarmMaskId) == softKeyMasks.end())
133 {
134 LOG_WARNING("[VTStateHelper] get_soft_key_mask: data/alarm mask '%lu' is not tracked", activeDataOrAlarmMask);
135 return NULL_OBJECT_ID;
136 }
137
138 return softKeyMasks.at(dataOrAlarmMaskId);
139 }
140
142 {
143 return (client != nullptr) && client->get_address_valid() && (client->get_address() == activeWorkingSetAddress);
144 }
145
146 void VirtualTerminalClientStateTracker::add_tracked_attribute(std::uint16_t objectId, std::uint8_t attribute, std::uint32_t initialValue)
147 {
148 if (attributeStates.find(objectId) == attributeStates.end())
149 {
150 attributeStates[objectId] = {};
151 }
152
153 auto &attributeMap = attributeStates.at(objectId);
154 if (attributeMap.find(attribute) != attributeMap.end())
155 {
156 LOG_WARNING("[VTStateHelper] add_tracked_attribute: attribute '%lu' of objectId '%lu' already tracked", attribute, objectId);
157 return;
158 }
159
160 attributeMap[attribute] = initialValue;
161 }
162
163 void VirtualTerminalClientStateTracker::remove_tracked_attribute(std::uint16_t objectId, std::uint8_t attribute)
164 {
165 if (attributeStates.find(objectId) == attributeStates.end())
166 {
167 LOG_WARNING("[VTStateHelper] remove_tracked_attribute: objectId '%lu' was not tracked", objectId);
168 return;
169 }
170
171 auto &attributeMap = attributeStates.at(objectId);
172 if (attributeMap.find(attribute) == attributeMap.end())
173 {
174 LOG_WARNING("[VTStateHelper] remove_tracked_attribute: attribute '%lu' of objectId '%lu' was not tracked", attribute, objectId);
175 return;
176 }
177
178 attributeMap.erase(attribute);
179 }
180
181 std::uint32_t VirtualTerminalClientStateTracker::get_attribute(std::uint16_t objectId, std::uint8_t attribute) const
182 {
183 if (attributeStates.find(objectId) == attributeStates.end())
184 {
185 LOG_WARNING("[VTStateHelper] get_attribute: objectId '%lu' not tracked", objectId);
186 return 0;
187 }
188
189 const auto &attributeMap = attributeStates.at(objectId);
190 if (attributeMap.find(attribute) == attributeMap.end())
191 {
192 LOG_WARNING("[VTStateHelper] get_attribute: attribute '%lu' of objectId '%lu' not tracked", attribute, objectId);
193 return 0;
194 }
195
196 return attributeMap.at(attribute);
197 }
198
200 {
201 if (activeDataOrAlarmMask != maskId)
202 {
203 // Add the current active mask to the history if it is valid
205 {
208 {
209 dataAndAlarmMaskHistory.pop_back();
210 }
211 }
212 // Update the active mask
213 activeDataOrAlarmMask = maskId;
214 }
215 }
216
218 {
219 if ((!message.has_valid_source_control_function()) || (message.get_data_length() == 0))
220 {
221 // We are not interested in messages without a valid source control function or without data
222 return;
223 }
224
225 auto *parent = static_cast<VirtualTerminalClientStateTracker *>(parentPointer);
226 if (message.is_broadcast() &&
227 message.is_parameter_group_number(CANLibParameterGroupNumber::VirtualTerminalToECU) &&
228 (message.get_uint8_at(0) == static_cast<std::uint8_t>(VirtualTerminalClient::Function::VTStatusMessage)))
229 {
230 parent->process_status_message(message);
231 }
232 if (message.is_source(parent->server) && (!message.is_broadcast()) && message.is_parameter_group_number(CANLibParameterGroupNumber::VirtualTerminalToECU))
233 {
234 parent->process_message_from_connected_server(message);
235 }
236 else if (message.is_destination(parent->server) && (!message.is_broadcast()) && message.is_parameter_group_number(CANLibParameterGroupNumber::ECUtoVirtualTerminal))
237 {
238 parent->process_message_to_connected_server(message);
239 }
240 }
241
243 {
244 if (CAN_DATA_LENGTH == message.get_data_length())
245 {
248 {
252 {
253 std::uint16_t softKeyMask = message.get_uint16_at(4);
254 softKeyMasks[activeDataOrAlarmMask] = softKeyMask;
255 }
256 }
257 }
258 }
259
261 {
262 std::uint8_t function = message.get_uint8_at(0);
263 switch (function)
264 {
265 case static_cast<std::uint8_t>(VirtualTerminalClient::Function::VTStatusMessage):
266 {
270 {
273 {
274 std::uint16_t softKeyMask = message.get_uint16_at(4);
275 softKeyMasks[activeDataOrAlarmMask] = softKeyMask;
276 }
277 }
278 }
279 break;
280
281 case static_cast<std::uint8_t>(VirtualTerminalClient::Function::ChangeActiveMaskCommand):
282 {
283 auto errorCode = message.get_uint8_at(3);
284 if (errorCode == 0)
285 {
287 }
288 }
289 break;
290
291 case static_cast<std::uint8_t>(VirtualTerminalClient::Function::ChangeSoftKeyMaskCommand):
292 {
293 auto errorCode = message.get_uint8_at(3);
294 if (errorCode == 0)
295 {
296 std::uint16_t associatedMask = message.get_uint16_at(1);
297 std::uint16_t softKeyMask = message.get_uint16_at(4);
298 if (softKeyMasks.find(associatedMask) != softKeyMasks.end())
299 {
300 softKeyMasks[associatedMask] = softKeyMask;
301 }
302 }
303 }
304 break;
305
306 case static_cast<std::uint8_t>(VirtualTerminalClient::Function::ChangeNumericValueCommand):
307 {
308 if (CAN_DATA_LENGTH == message.get_data_length())
309 {
310 auto errorCode = message.get_uint8_at(3);
311 if (errorCode == 0)
312 {
313 std::uint16_t objectId = message.get_uint16_at(1);
314 if (numericValueStates.find(objectId) != numericValueStates.end())
315 {
316 std::uint32_t value = message.get_uint32_at(4);
317 numericValueStates[objectId] = value;
318 }
319 }
320 }
321 }
322 break;
323
324 case static_cast<std::uint8_t>(VirtualTerminalClient::Function::VTChangeNumericValueMessage):
325 {
326 if (CAN_DATA_LENGTH == message.get_data_length())
327 {
328 std::uint16_t objectId = message.get_uint16_at(1);
329 if (numericValueStates.find(objectId) != numericValueStates.end())
330 {
331 std::uint32_t value = message.get_uint32_at(4);
332 numericValueStates[objectId] = value;
333 }
334 }
335 }
336 break;
337
338 case static_cast<std::uint8_t>(VirtualTerminalClient::Function::ChangeAttributeCommand):
339 {
340 if (CAN_DATA_LENGTH == message.get_data_length())
341 {
342 auto errorCode = message.get_uint8_at(4);
343 if (errorCode == 0)
344 {
345 std::uint16_t objectId = message.get_uint16_at(1);
346 std::uint8_t attribute = message.get_uint8_at(3);
347 std::uint8_t error = message.get_uint8_at(4);
348
350 {
351 const auto &pendingCommand = pendingChangeAttributeCommands.at(message.get_source_control_function());
352 if ((pendingCommand.objectId == objectId) && (pendingCommand.attribute == attribute) && (0 == error))
353 {
354 std::uint32_t value = message.get_uint32_at(5);
355 attributeStates[objectId][attribute] = value;
356 }
358 }
359 }
360 }
361 }
362 break;
363
364 default:
365 break;
366 }
367 }
368
370 {
371 std::uint8_t function = message.get_uint8_at(0);
372 switch (function)
373 {
374 case static_cast<std::uint8_t>(VirtualTerminalClient::Function::ChangeAttributeCommand):
375 {
376 if (CAN_DATA_LENGTH == message.get_data_length())
377 {
378 std::uint16_t objectId = message.get_uint16_at(1);
379 std::uint8_t attribute = message.get_uint8_at(3);
380
381 // Only track the change if the attribute should be tracked
382 if ((attributeStates.find(objectId) != attributeStates.end()) &&
383 (attributeStates.at(objectId).find(attribute) != attributeStates.at(objectId).end()))
384 {
385 std::uint32_t value = message.get_uint32_at(4);
386 pendingChangeAttributeCommands[message.get_source_control_function()] = { value, objectId, attribute };
387 }
388 }
389 }
390 break;
391
392 default:
393 break;
394 }
395 }
396} // 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...
A class that represents a generic CAN message of arbitrary length.
bool is_source(std::shared_ptr< ControlFunction > controlFunction) const
Returns whether the message is originated from the control function.
bool is_destination(std::shared_ptr< ControlFunction > controlFunction) const
Returns whether the message is destined for the control function.
std::uint32_t get_data_length() const
Returns the length of the data in the CAN message.
bool has_valid_source_control_function() const
Returns whether the message is sent by a device that claimed its address on the bus.
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...
bool is_parameter_group_number(CANLibParameterGroupNumber parameterGroupNumber) const
Compares the identifier of the message to the parameter group number (PGN) supplied.
std::uint16_t get_uint16_at(const std::uint32_t index, const ByteFormat format=ByteFormat::LittleEndian) const
Get a 16-bit unsigned integer from the buffer at a specific index. A 16-bit unsigned integer can hold...
std::uint32_t get_uint32_at(const std::uint32_t index, const ByteFormat format=ByteFormat::LittleEndian) const
Get a 32-bit unsigned integer from the buffer at a specific index. A 32-bit unsigned integer can hold...
std::shared_ptr< ControlFunction > get_destination_control_function() const
Gets the destination control function that the message is to.
bool is_broadcast() const
Returns whether the message is sent as a broadcast message / to all devices on the bus.
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)
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.
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)
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...
A helper class to update and track the state of an active working set.
std::shared_ptr< ControlFunction > client
The control function of the virtual terminal client to track.
void add_tracked_attribute(std::uint16_t objectId, std::uint8_t attribute, std::uint32_t initialValue=0)
Adds an attribute to track.
void add_tracked_soft_key_mask(std::uint16_t dataOrAlarmMaskId, std::uint16_t initialSoftKeyMaskId=0)
Adds a data/alarm mask to track the soft key mask for.
void add_tracked_numeric_value(std::uint16_t objectId, std::uint32_t initialValue=0)
TODO: void initialize_with_defaults(ObjectPool &objectPool);.
std::uint32_t get_attribute(std::uint16_t objectId, std::uint8_t attribute) const
Get the value of an attribute of a tracked object.
std::map< std::shared_ptr< ControlFunction >, ChangeAttributeCommand > pendingChangeAttributeCommands
Holds the pending change attribute command for a control function.
static void process_rx_or_tx_message(const CANMessage &message, void *parentPointer)
Processes a received or transmitted message.
std::uint16_t activeDataOrAlarmMask
Holds the data/alarm mask currently visible on the server for this client.
void cache_active_mask(std::uint16_t maskId)
Cache a mask as the active mask on the server.
std::map< std::uint16_t, std::uint16_t > softKeyMasks
Holds the data/alarms masks with their associated soft keys masks for tracked objects.
std::deque< std::uint16_t > dataAndAlarmMaskHistory
Holds the history of data/alarm masks that were active on the server for this client.
void remove_tracked_soft_key_mask(std::uint16_t dataOrAlarmMaskId)
Removes a data/alarm mask from tracking the soft key mask for.
void remove_tracked_numeric_value(std::uint16_t objectId)
Removes a numeric value from tracking.
std::uint16_t get_active_soft_key_mask() const
Get the soft key mask currently active on thse server for this client. It may not be displayed if the...
std::uint16_t get_soft_key_mask(std::uint16_t dataOrAlarmMaskId) const
Get the soft key mask currently associated with a data/alarm mask.
std::map< std::uint16_t, std::map< std::uint8_t, std::uint32_t > > attributeStates
void process_message_from_connected_server(const CANMessage &message)
Processes a VT->ECU message received by any client, sent from the connected server.
const std::deque< std::uint16_t > & get_mask_history() const
Get the history of data/alarm masks that were active on the server for this client.
void remove_tracked_attribute(std::uint16_t objectId, std::uint8_t attribute)
Removes an attribute from tracking.
bool is_working_set_active() const
Get whether the working set of the client is active on the server.
void set_max_mask_history_size(std::size_t size)
Sets the maximum size of the data/alarm mask history (default: 100)
std::shared_ptr< ControlFunction > server
The control function of the server the client is connected to.
void process_message_to_connected_server(const CANMessage &message)
Processes a ECU->VT message received by the connected server, sent from any control function.
void process_status_message(const CANMessage &message)
Processes a status message from a VT server.
VirtualTerminalClientStateTracker(std::shared_ptr< ControlFunction > client)
The constructor to track the state of an active working set provided by a client.
std::uint8_t activeWorkingSetAddress
Holds the address of the control function that currently has.
std::uint32_t get_numeric_value(std::uint16_t objectId) const
Gets the current numeric value of a tracked object.
std::size_t maxDataAndAlarmMaskHistorySize
Holds the maximum size of the data/alarm mask history.
std::size_t get_max_mask_history_size() const
Get the maximum size of the data/alarm mask history.
std::uint16_t get_active_mask() const
Get the data/alarm mask currently active on the server for this client. It may not be displayed if th...
A class to manage a client connection to a ISOBUS virtual terminal display.
A helper class to track the state of an active working set.
This namespace encompasses all of the ISO11783 stack's functionality to reduce global namespace pollu...
constexpr std::uint16_t NULL_OBJECT_ID
Special ID used to indicate no object.
constexpr std::uint8_t CAN_DATA_LENGTH
The length of a classical CAN frame.