AgIsoStack++
A control-function-focused implementation of the major ISOBUS and J1939 protocols
Loading...
Searching...
No Matches
isobus_task_controller_server.cpp
Go to the documentation of this file.
1//================================================================================================
10//================================================================================================
12
16#include "isobus/utility/system_timing.hpp"
17
18#include <cassert>
19
20namespace isobus
21{
22 TaskControllerServer::TaskControllerServer(std::shared_ptr<InternalControlFunction> internalControlFunction,
23 std::uint8_t numberBoomsSupported,
24 std::uint8_t numberSectionsSupported,
25 std::uint8_t numberChannelsSupportedForPositionBasedControl,
26 const TaskControllerOptions &options,
27 TaskControllerVersion versionToReport) :
28 languageCommandInterface(internalControlFunction, true),
29 serverControlFunction(internalControlFunction),
30 reportedVersion(versionToReport),
31 numberBoomsSupportedToReport(numberBoomsSupported),
32 numberSectionsSupportedToReport(numberSectionsSupported),
33 numberChannelsSupportedForPositionBasedControlToReport(numberChannelsSupportedForPositionBasedControl),
34 optionsBitfieldToReport(options.get_bitfield())
35 {
36 }
37
42
43 bool TaskControllerServer::send_request_value(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber) const
44 {
45 std::array<std::uint8_t, CAN_DATA_LENGTH> payload = { 0 };
46
47 payload[0] = (static_cast<std::uint8_t>(ProcessDataCommands::RequestValue) & 0x0F);
48 payload[0] |= (elementNumber & 0x0F) << 4;
49 payload[1] = static_cast<std::uint8_t>(elementNumber >> 4);
50 payload[2] = static_cast<std::uint8_t>(dataDescriptionIndex);
51 payload[3] = static_cast<std::uint8_t>(dataDescriptionIndex >> 8);
52 payload[4] = 0xFF;
53 payload[5] = 0xFF;
54 payload[6] = 0xFF;
55 payload[7] = 0xFF;
56 return send_process_data_to_client(clientControlFunction,
57 payload.data(),
58 payload.size());
59 }
60
61 bool TaskControllerServer::send_time_interval_measurement_command(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t timeInterval) const
62 {
63 return send_measurement_command(clientControlFunction, static_cast<std::uint8_t>(ProcessDataCommands::MeasurementTimeInterval), dataDescriptionIndex, elementNumber, timeInterval);
64 }
65
66 bool TaskControllerServer::send_distance_interval_measurement_command(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t distanceInterval) const
67 {
68 return send_measurement_command(clientControlFunction, static_cast<std::uint8_t>(ProcessDataCommands::MeasurementDistanceInterval), dataDescriptionIndex, elementNumber, distanceInterval);
69 }
70
71 bool TaskControllerServer::send_minimum_threshold_measurement_command(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t minimum) const
72 {
73 return send_measurement_command(clientControlFunction, static_cast<std::uint8_t>(ProcessDataCommands::MeasurementMinimumWithinThreshold), dataDescriptionIndex, elementNumber, minimum);
74 }
75
76 bool TaskControllerServer::send_maximum_threshold_measurement_command(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t maximum) const
77 {
78 return send_measurement_command(clientControlFunction, static_cast<std::uint8_t>(ProcessDataCommands::MeasurementMaximumWithinThreshold), dataDescriptionIndex, elementNumber, maximum);
79 }
80
81 bool TaskControllerServer::send_change_threshold_measurement_command(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t threshold) const
82 {
83 return send_measurement_command(clientControlFunction, static_cast<std::uint8_t>(ProcessDataCommands::MeasurementChangeThreshold), dataDescriptionIndex, elementNumber, threshold);
84 }
85
86 bool TaskControllerServer::send_set_value_and_acknowledge(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t processDataValue) const
87 {
88 return send_measurement_command(clientControlFunction, static_cast<std::uint8_t>(ProcessDataCommands::SetValueAndAcknowledge), dataDescriptionIndex, elementNumber, processDataValue);
89 }
90
91 bool TaskControllerServer::send_set_value(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t processDataValue) const
92 {
93 return send_measurement_command(clientControlFunction, static_cast<std::uint8_t>(ProcessDataCommands::Value), dataDescriptionIndex, elementNumber, processDataValue);
94 }
95
97 {
98 if (isTaskActive != get_task_totals_active())
99 {
100 currentStatusByte &= ~static_cast<std::uint8_t>(ServerStatusBit::TaskTotalsActive);
101 currentStatusByte |= (static_cast<std::uint8_t>(isTaskActive) & static_cast<std::uint8_t>(ServerStatusBit::TaskTotalsActive));
102 lastStatusMessageTimestamp_ms = 0; // Force a status message to be sent on the next update.
103 }
104 }
105
107 {
108 return (0 != (currentStatusByte & static_cast<std::uint8_t>(ServerStatusBit::TaskTotalsActive)));
109 }
110
112 {
113 if (!initialized)
114 {
115 assert(nullptr != serverControlFunction); // You can't have a server without a control function to send messages from! That wouldn't make any sense.
117 CANNetworkManager::CANNetwork.add_any_control_function_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ProcessData), store_rx_message, this);
118 CANNetworkManager::CANNetwork.add_any_control_function_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::WorkingSetMaster), store_rx_message, this);
119 initialized = true;
120 }
121 }
122
124 {
125 return initialized;
126 }
127
129 {
130 if (initialized)
131 {
132 initialized = false;
133 CANNetworkManager::CANNetwork.remove_any_control_function_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ProcessData), store_rx_message, this);
134 CANNetworkManager::CANNetwork.remove_any_control_function_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::WorkingSetMaster), store_rx_message, this);
135 }
136 }
137
142
143#if !defined CAN_STACK_DISABLE_THREADS && !defined ARDUINO
145 {
147 }
148#endif
149
151 {
153 if ((true == SystemTiming::time_expired_ms(lastStatusMessageTimestamp_ms, STATUS_MESSAGE_RATE_MS)) &&
154 (true == send_status_message()))
155 {
156 lastStatusMessageTimestamp_ms = SystemTiming::get_timestamp_ms();
157 }
158
159 // Remove any clients that have timed out.
160 activeClients.erase(std::remove_if(activeClients.begin(),
161 activeClients.end(),
162 [](std::shared_ptr<ActiveClient> clientInfo) {
163 constexpr std::uint32_t CLIENT_TASK_TIMEOUT_MS = 6000;
164 if (SystemTiming::time_expired_ms(clientInfo->lastStatusMessageTimestamp_ms, CLIENT_TASK_TIMEOUT_MS))
165 {
166 LOG_WARNING("[TC Server]: Client 0x%02X has timed out. Removing from active client list.", clientInfo->clientControlFunction->get_address());
167 return true;
168 }
169 return false;
170 }),
171 activeClients.end());
172 }
173
174 TaskControllerServer::ActiveClient::ActiveClient(std::shared_ptr<ControlFunction> clientControlFunction) :
175 clientControlFunction(clientControlFunction),
176 lastStatusMessageTimestamp_ms(SystemTiming::get_timestamp_ms())
177 {
178 }
179
180 void TaskControllerServer::store_rx_message(const CANMessage &message, void *parentPointer)
181 {
182 if (nullptr != parentPointer)
183 {
184 auto server = static_cast<TaskControllerServer *>(parentPointer);
185#if !defined CAN_STACK_DISABLE_THREADS && !defined ARDUINO
186 const std::lock_guard<std::mutex> lock(server->messagesMutex);
187 server->rxMessageQueue.push_back(message);
188 server->updateWakeupCondition.notify_all();
189#else
190 server->rxMessageQueue.push_back(message);
191#endif
192 }
193 }
194
196 {
197#if !defined CAN_STACK_DISABLE_THREADS && !defined ARDUINO
198 const std::lock_guard<std::mutex> lock(messagesMutex);
199#endif
200 while (!rxMessageQueue.empty())
201 {
202 const auto &rxMessage = rxMessageQueue.front();
203 auto &rxData = rxMessage.get_data();
204
205 switch (rxMessage.get_identifier().get_parameter_group_number())
206 {
207 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::ProcessData):
208 {
209 switch (static_cast<ProcessDataCommands>(rxData[0] & 0x0F))
210 {
212 {
213 if ((rxData[0] >> 4) <= static_cast<std::uint8_t>(TechnicalDataCommandParameters::IdentifyTaskController))
214 {
215 switch (static_cast<TechnicalDataCommandParameters>(rxData[0] >> 4))
216 {
217 case TechnicalDataCommandParameters::RequestVersion:
218 {
219 if (serverControlFunction == rxMessage.get_destination_control_function())
220 {
221 send_version(rxMessage.get_source_control_function());
222 send_generic_process_data_default_payload(static_cast<std::uint8_t>(TechnicalDataCommandParameters::RequestVersion), rxMessage.get_source_control_function());
223 }
224 }
225 break;
226
228 {
229 if (CAN_DATA_LENGTH == rxMessage.get_data_length())
230 {
231 // Not sure if we care about this one, since we'll treat clients the same regardless.
232 LOG_DEBUG("[TC Server]: Client reports that its version is %u", rxData[1]);
233 }
234 }
235 break;
236
238 {
239 LOG_INFO("[TC Server]: Received identify task controller command from 0x%02X. We are TC number %u", rxMessage.get_source_control_function()->get_address(), serverControlFunction->get_NAME().get_function_instance());
240 if (serverControlFunction == rxMessage.get_destination_control_function())
241 {
242 send_generic_process_data_default_payload(rxData[0], rxMessage.get_source_control_function());
243 identify_task_controller(serverControlFunction->get_NAME().get_function_instance() + 1);
244 }
245 else
246 {
247 // No response needed for a global request.
248 identify_task_controller(serverControlFunction->get_NAME().get_function_instance() + 1);
249 }
250 }
251 break;
252 }
253 }
254 else
255 {
256 LOG_WARNING("[TC Server]: Unknown technical capabilities command received: 0x%02X", rxData[0]);
257 }
258 }
259 break;
260
262 {
263 if ((rxData[0] >> 4) <= static_cast<std::uint8_t>(DeviceDescriptorCommandParameters::ChangeDesignatorResponse))
264 {
265 if ((rxMessage.get_data_length() >= CAN_DATA_LENGTH) &&
266 (nullptr != rxMessage.get_source_control_function()) &&
267 (nullptr != rxMessage.get_destination_control_function()))
268 {
269 switch (static_cast<DeviceDescriptorCommandParameters>(rxData[0] >> 4))
270 {
271 case DeviceDescriptorCommandParameters::RequestStructureLabel:
272 {
273 if (nullptr != get_active_client(rxMessage.get_source_control_function()))
274 {
275 std::vector<std::uint8_t> structureLabel;
276 std::vector<std::uint8_t> extendedStructureLabel;
277
278 for (std::uint8_t i = 0; i < CAN_DATA_LENGTH - 1; i++)
279 {
280 structureLabel.push_back(rxData[i + 1]);
281 }
282
283 if (rxMessage.get_data_length() > CAN_DATA_LENGTH)
284 {
285 // If the length is greater than 8, then an extended label is being requested.
286 for (std::size_t i = 0; i < (rxMessage.get_data_length() - CAN_DATA_LENGTH); i++)
287 {
288 extendedStructureLabel.push_back(rxData[CAN_DATA_LENGTH + i]);
289 }
290 }
291
292 if (get_is_stored_device_descriptor_object_pool_by_structure_label(rxMessage.get_source_control_function(), structureLabel, extendedStructureLabel))
293 {
294 LOG_INFO("[TC Server]:Client 0x%02X structure label(s) matched.", rxMessage.get_source_control_function()->get_address());
295 send_structure_label(rxMessage.get_source_control_function(), structureLabel, extendedStructureLabel);
296 }
297 else
298 {
299 // No object pool found. Send FFs as the structure label.
300 LOG_INFO("[TC Server]:Client 0x%02X structure label(s) did not match. Sending 0xFFs as the structure label.", rxMessage.get_source_control_function()->get_address());
301 send_generic_process_data_default_payload((static_cast<std::uint8_t>(ProcessDataCommands::DeviceDescriptor) | (static_cast<std::uint8_t>(DeviceDescriptorCommandParameters::StructureLabel) << 4)), rxMessage.get_source_control_function());
302 }
303 }
304 else
305 {
306 nack_process_data_command(rxMessage.get_source_control_function());
307 }
308 }
309 break;
310
312 {
313 if (nullptr != get_active_client(rxMessage.get_source_control_function()))
314 {
315 const std::array<std::uint8_t, 7> localizationLabel = { rxData.at(1), rxData.at(2), rxData.at(3), rxData.at(4), rxData.at(5), rxData.at(6), rxData.at(7) };
316 if (get_is_stored_device_descriptor_object_pool_by_localization_label(rxMessage.get_source_control_function(), localizationLabel))
317 {
318 LOG_INFO("[TC Server]:Client 0x%02X localization label matched.", rxMessage.get_source_control_function()->get_address());
319 send_localization_label(rxMessage.get_source_control_function(), localizationLabel);
320 }
321 else
322 {
323 // No object pool found. Send FFs as the localization label.
324 LOG_INFO("[TC Server]: No object pool found for client 0x%02X localization label. Sending FFs as the localization label.", rxMessage.get_source_control_function()->get_address());
325 send_generic_process_data_default_payload(static_cast<std::uint8_t>(ProcessDataCommands::DeviceDescriptor) | static_cast<std::uint8_t>(DeviceDescriptorCommandParameters::LocalizationLabel) << 4, rxMessage.get_source_control_function());
326 }
327 }
328 else
329 {
330 nack_process_data_command(rxMessage.get_source_control_function());
331 }
332 }
333 break;
334
336 {
337 if (nullptr != get_active_client(rxMessage.get_source_control_function()))
338 {
339 std::uint32_t requestedSize = rxMessage.get_uint32_at(1);
340
341 if ((requestedSize <= CANMessage::ABSOLUTE_MAX_MESSAGE_LENGTH) &&
342 (get_is_enough_memory_available(requestedSize)))
343 {
344 LOG_INFO("[TC Server]: Client 0x%02X requests object pool transfer of %u bytes", rxMessage.get_source_control_function()->get_address(), requestedSize);
345
346 get_active_client(rxMessage.get_source_control_function())->clientDDOPsize_bytes = requestedSize;
347 send_request_object_pool_transfer_response(rxMessage.get_source_control_function(), true);
348 }
349 else
350 {
351 LOG_ERROR("[TC Server]: Client 0x%02X requests object pool transfer of %u bytes but there is not enough memory available.", rxMessage.get_source_control_function()->get_address(), requestedSize);
352 send_request_object_pool_transfer_response(rxMessage.get_source_control_function(), false);
353 }
354 }
355 else
356 {
357 nack_process_data_command(rxMessage.get_source_control_function());
358 }
359 }
360 break;
361
363 {
364 if (nullptr != get_active_client(rxMessage.get_source_control_function()))
365 {
366 std::vector<std::uint8_t> objectPool = rxData;
367 objectPool.erase(objectPool.begin()); // Strip the command byte from the front of the object pool
368
369 if (0 == get_active_client(rxMessage.get_source_control_function())->clientDDOPsize_bytes)
370 {
371 LOG_WARNING("[TC Server]: Client 0x%02X sent object pool transfer without first requesting a transfer!", rxMessage.get_source_control_function()->get_address());
372 }
373
374 if (store_device_descriptor_object_pool(rxMessage.get_source_control_function(), objectPool, 0 != get_active_client(rxMessage.get_source_control_function())->numberOfObjectPoolSegments))
375 {
376 LOG_INFO("[TC Server]: Stored DDOP segment for client 0x%02X", rxMessage.get_source_control_function()->get_address());
377 send_object_pool_transfer_response(rxMessage.get_source_control_function(), 0, static_cast<std::uint32_t>(objectPool.size())); // No error, transfer OK
378 }
379 else
380 {
381 LOG_ERROR("[TC Server]: Failed to store DDOP segment for client 0x%02X. Reporting to the client as \"Any other error\"", rxMessage.get_source_control_function()->get_address());
382 send_object_pool_transfer_response(rxMessage.get_source_control_function(), 2, static_cast<std::uint32_t>(objectPool.size()));
383 }
384 }
385 else
386 {
387 nack_process_data_command(rxMessage.get_source_control_function());
388 }
389 }
390 break;
391
393 {
394 if (nullptr != get_active_client(rxMessage.get_source_control_function()))
395 {
396 constexpr std::uint8_t ACTIVATE = 0xFF;
397 constexpr std::uint8_t DEACTIVATE = 0x00;
398 ObjectPoolActivationError activationError = ObjectPoolActivationError::NoErrors;
399 ObjectPoolErrorCodes errorCode = ObjectPoolErrorCodes::NoErrors;
400 std::uint16_t faultingParentObject = 0;
401 std::uint16_t faultingObject = 0;
402
403 if (ACTIVATE == rxData[1])
404 {
405 LOG_INFO("[TC Server]: Client 0x%02X requests activation of object pool", rxMessage.get_source_control_function()->get_address());
406 auto client = get_active_client(rxMessage.get_source_control_function());
407
408 if (activate_object_pool(rxMessage.get_source_control_function(), activationError, errorCode, faultingParentObject, faultingObject))
409 {
410 LOG_INFO("[TC Server]: Object pool activated for client 0x%02X", rxMessage.get_source_control_function()->get_address());
411 client->isDDOPActive = true;
412 send_object_pool_activate_deactivate_response(rxMessage.get_source_control_function(), 0, 0, 0xFFFF, 0xFFFF);
413 }
414 else
415 {
416 LOG_ERROR("[TC Server]: Failed to activate object pool for client 0x%02X. Error code: %u, Faulty object: %u, Parent of faulty object: %u", rxMessage.get_source_control_function()->get_address(), static_cast<std::uint8_t>(activationError), faultingObject, faultingParentObject);
417 send_object_pool_activate_deactivate_response(rxMessage.get_source_control_function(), static_cast<std::uint8_t>(activationError), static_cast<std::uint8_t>(errorCode), faultingParentObject, faultingObject);
418 }
419 }
420 else if (DEACTIVATE == rxData[1])
421 {
422 LOG_INFO("[TC Server]: Client 0x%02X requests deactivation of object pool", rxMessage.get_source_control_function()->get_address());
423
424 if (deactivate_object_pool(rxMessage.get_source_control_function()))
425 {
426 LOG_INFO("[TC Server]: Object pool deactivated for client 0x%02X", rxMessage.get_source_control_function()->get_address());
427 get_active_client(rxMessage.get_source_control_function())->isDDOPActive = false;
428 send_object_pool_activate_deactivate_response(rxMessage.get_source_control_function(), 0, 0, 0xFFFF, 0xFFFF);
429 }
430 else
431 {
432 LOG_ERROR("[TC Server]: Failed to deactivate object pool for client 0x%02X", rxMessage.get_source_control_function()->get_address());
433 send_object_pool_activate_deactivate_response(rxMessage.get_source_control_function(), static_cast<std::uint8_t>(ObjectPoolActivationError::AnyOtherError), 0, 0xFFFF, 0xFFFF);
434 }
435 }
436 else
437 {
438 LOG_ERROR("[TC Server]: Client 0x%02X requests activation/deactivation of object pool with invalid value: 0x%02X", rxMessage.get_source_control_function()->get_address(), rxData[1]);
439 }
440 }
441 else
442 {
443 nack_process_data_command(rxMessage.get_source_control_function());
444 }
445 }
446 break;
447
449 {
450 if (nullptr != get_active_client(rxMessage.get_source_control_function()))
451 {
452 ObjectPoolDeletionErrors errorCode = ObjectPoolDeletionErrors::ErrorDetailsNotAvailable;
453
454 if (delete_device_descriptor_object_pool(rxMessage.get_source_control_function(), errorCode))
455 {
456 LOG_INFO("[TC Server]: Deleted object pool for client 0x%02X", rxMessage.get_source_control_function()->get_address());
457 send_delete_object_pool_response(rxMessage.get_source_control_function(), true, static_cast<std::uint8_t>(ObjectPoolDeletionErrors::ErrorDetailsNotAvailable));
458 }
459 else
460 {
461 LOG_ERROR("[TC Server]: Failed to delete object pool for client 0x%02X. Error code: %u", rxMessage.get_source_control_function()->get_address(), static_cast<std::uint8_t>(errorCode));
462 send_delete_object_pool_response(rxMessage.get_source_control_function(), false, static_cast<std::uint8_t>(errorCode));
463 }
464 }
465 else
466 {
467 nack_process_data_command(rxMessage.get_source_control_function());
468 }
469 }
470 break;
471
473 {
474 if (nullptr != get_active_client(rxMessage.get_source_control_function()))
475 {
476 if (get_active_client(rxMessage.get_source_control_function())->isDDOPActive)
477 {
478 std::uint16_t objectID = rxMessage.get_uint16_at(1);
479 std::vector<std::uint8_t> newDesignatorUTF8Bytes;
480
481 for (std::size_t i = 0; i < rxData.size() - 3; i++)
482 {
483 newDesignatorUTF8Bytes.push_back(rxData[3 + i]);
484 }
485
486 if (change_designator(rxMessage.get_source_control_function(), objectID, newDesignatorUTF8Bytes))
487 {
488 LOG_INFO("[TC Server]: Changed designator for client 0x%02X. Object ID: %u", rxMessage.get_source_control_function()->get_address(), objectID);
489 send_change_designator_response(rxMessage.get_source_control_function(), objectID, 0);
490 }
491 else
492 {
493 LOG_ERROR("[TC Server]: Failed to change designator for client 0x%02X. Object ID: %u", rxMessage.get_source_control_function()->get_address(), objectID);
494 send_change_designator_response(rxMessage.get_source_control_function(), objectID, 1);
495 }
496 }
497 else
498 {
499 LOG_ERROR("[TC Server]: Client 0x%02X requests change to change a designator but the object pool is not active.", rxMessage.get_source_control_function()->get_address());
500 }
501 }
502 else
503 {
504 nack_process_data_command(rxMessage.get_source_control_function());
505 }
506 }
507 break;
508
516 {
517 // Nack server side messages
518 nack_process_data_command(rxMessage.get_source_control_function());
519 }
520 break;
521 }
522 }
523 else
524 {
525 LOG_WARNING("[TC Server]: Device descriptor message received with invalid DLC. DLC must be at least 8.");
526 }
527 }
528 else
529 {
530 LOG_WARNING("[TC Server]: Unknown device descriptor command received: 0x%02X", rxData[0]);
531 }
532 }
533 break;
534
537 {
538 if (nullptr != get_active_client(rxMessage.get_source_control_function()))
539 {
540 if (get_active_client(rxMessage.get_source_control_function())->isDDOPActive)
541 {
542 std::uint16_t DDI = rxMessage.get_uint16_at(2);
543 std::uint16_t elementNumber = static_cast<std::uint16_t>(rxData[0] >> 4) | (static_cast<std::uint16_t>(rxData[1]) << 4);
544 std::int32_t processVariableValue = rxMessage.get_int32_at(4);
545 std::uint8_t errorCodes = 0;
546
547 if (on_value_command(rxMessage.get_source_control_function(), DDI, elementNumber, processVariableValue, errorCodes))
548 {
549 LOG_DEBUG("[TC Server]: Client 0x%02X value command for element %u DDI %u and value %d OK.", rxMessage.get_source_control_function()->get_address(), elementNumber, DDI, processVariableValue);
550
551 if (ProcessDataCommands::SetValueAndAcknowledge == static_cast<ProcessDataCommands>(rxData[0] & 0x0F))
552 {
553 send_process_data_acknowledge(rxMessage.get_source_control_function(), DDI, elementNumber, 0, static_cast<ProcessDataCommands>(rxData[0] & 0x0F));
554 }
555 }
556 else
557 {
558 LOG_ERROR("[TC Server]: Client 0x%02X value command for element %u DDI %u and value %d failed.", rxMessage.get_source_control_function()->get_address(), elementNumber, DDI, processVariableValue);
559
560 if (0 == errorCodes)
561 {
562 LOG_ERROR("[TC Server]: Your derived TC server class must set errorCodes to a non-zero value if a value command fails.");
563 errorCodes = static_cast<std::uint8_t>(ProcessDataAcknowledgeErrorCodes::DDINotSupportedByElement); // Like this!
564 assert(false); // See above error message.
565 }
566 send_process_data_acknowledge(rxMessage.get_source_control_function(), DDI, elementNumber, errorCodes, static_cast<ProcessDataCommands>(rxData[0] & 0x0F));
567 }
568 }
569 else
570 {
571 LOG_ERROR("[TC Server]: Client 0x%02X sent a value command but the object pool is not active.", rxMessage.get_source_control_function()->get_address());
572 }
573 }
574 else
575 {
576 nack_process_data_command(rxMessage.get_source_control_function());
577 }
578 }
579 break;
580
582 {
583 if (nullptr != get_active_client(rxMessage.get_source_control_function()))
584 {
585 std::uint16_t DDI = rxMessage.get_uint16_at(2);
586 std::uint16_t elementNumber = static_cast<std::uint16_t>(rxData[0] >> 4) | (static_cast<std::uint16_t>(rxData[1]) << 4);
587
588 if (get_active_client(rxMessage.get_source_control_function())->isDDOPActive)
589 {
590 on_process_data_acknowledge(rxMessage.get_source_control_function(), DDI, elementNumber, rxData[4], static_cast<ProcessDataCommands>(rxData[0] & 0x0F));
591 }
592 else
593 {
594 LOG_ERROR("[TC Server]: Client 0x%02X sent an acknowledge command but the object pool is not active.", rxMessage.get_source_control_function()->get_address());
595 send_process_data_acknowledge(rxMessage.get_source_control_function(), DDI, elementNumber, static_cast<std::uint8_t>(ProcessDataAcknowledgeErrorCodes::ProcessDataNotSettable), static_cast<ProcessDataCommands>(rxData[0] & 0x0F));
596 }
597 }
598 else
599 {
600 nack_process_data_command(rxMessage.get_source_control_function());
601 }
602 }
603 break;
604
610 {
611 if (CAN_DATA_LENGTH == rxMessage.get_data_length())
612 {
613 std::uint16_t DDI = static_cast<std::uint16_t>(rxData[2]) | (static_cast<std::uint16_t>(rxData[3]) << 8);
614 std::uint16_t elementNumber = static_cast<std::uint16_t>(rxData[0] >> 4) | (static_cast<std::uint16_t>(rxData[1]) << 4);
615 LOG_ERROR("[TC Server]: Client 0x%02X is sending measurement commands?", rxMessage.get_source_control_function()->get_address());
616 send_process_data_acknowledge(rxMessage.get_source_control_function(), DDI, elementNumber, static_cast<std::uint8_t>(ProcessDataAcknowledgeErrorCodes::ProcessDataCommandNotSupported), static_cast<ProcessDataCommands>(rxData[0] & 0x0F));
617 }
618 else
619 {
620 LOG_ERROR("[TC Server]: Client 0x%02X is sending measurement commands with invalid lengths, which is very unusual.", rxMessage.get_source_control_function()->get_address());
621 }
622 }
623 break;
624
627 {
628 // Ignore server side messages
629 }
630 break;
631
633 {
634 if (CAN_DATA_LENGTH == rxMessage.get_data_length())
635 {
636 for (const auto &activeClient : activeClients)
637 {
638 if ((nullptr != activeClient) &&
639 (activeClient->clientControlFunction == rxMessage.get_source_control_function()))
640 {
641 std::uint32_t status = rxData[4];
642 status |= static_cast<std::uint32_t>(rxData[5]) << 8;
643 status |= static_cast<std::uint32_t>(rxData[6]) << 16;
644 status |= static_cast<std::uint32_t>(rxData[7]) << 24;
645 activeClient->lastStatusMessageTimestamp_ms = SystemTiming::get_timestamp_ms();
646 activeClient->statusBitfield = status;
647 }
648 }
649 }
650 else
651 {
652 LOG_WARNING("[TC Server]: client task message received with invalid DLC. DLC must be 8.");
653 }
654 }
655 break;
656
658 {
659 LOG_WARNING("[TC Server]: Peer Control is currently not supported");
660 }
661 break;
662
665 {
666 LOG_WARNING("[TC Server]: Reserved command received: 0x%02X", rxData[0]);
667 }
668 break;
669
670 default:
671 {
672 LOG_WARNING("[TC Server]: Unknown ProcessData command received: 0x%02X", rxData[0]);
673 }
674 break;
675 }
676 }
677 break;
678
679 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::WorkingSetMaster):
680 {
681 if (CAN_DATA_LENGTH == rxMessage.get_data_length())
682 {
683 std::uint8_t numberOfWorkingSetMembers = rxData[0];
684
685 if (1 == numberOfWorkingSetMembers)
686 {
687 if (nullptr == get_active_client(rxMessage.get_source_control_function()))
688 {
689 activeClients.push_back(std::make_shared<ActiveClient>(rxMessage.get_source_control_function()));
690 }
691 }
692 else
693 {
694 LOG_ERROR("[TC Server]: Working set master message received with unsupported number of working set members: %u", numberOfWorkingSetMembers);
695 }
696 }
697 else
698 {
699 LOG_ERROR("[TC Server]: Working set master message received with invalid DLC. DLC should be 8.");
700 }
701 }
702 break;
703
704 default:
705 {
706 }
707 break;
708 }
709 rxMessageQueue.pop_front();
710 }
711 }
712
713 bool TaskControllerServer::send_generic_process_data_default_payload(std::uint8_t multiplexer, std::shared_ptr<ControlFunction> destination) const
714 {
715 std::array<std::uint8_t, CAN_DATA_LENGTH> payload = { multiplexer, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
717
718 switch (multiplexer & 0x0F)
719 {
720 case static_cast<std::uint8_t>(ProcessDataCommands::Value):
721 case static_cast<std::uint8_t>(ProcessDataCommands::SetValueAndAcknowledge):
722 case static_cast<std::uint8_t>(ProcessDataCommands::Status):
723 case static_cast<std::uint8_t>(ProcessDataCommands::ClientTask):
724 {
726 }
727 break;
728
729 case static_cast<std::uint8_t>(ProcessDataCommands::Acknowledge):
730 {
732 }
733 break;
734
735 default:
736 break;
737 }
738 return send_process_data_to_client(destination,
739 payload.data(),
740 payload.size(),
741 priority);
742 }
743
744 bool TaskControllerServer::send_measurement_command(std::shared_ptr<ControlFunction> clientControlFunction, std::uint8_t commandValue, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t processDataValue) const
745 {
746 bool retVal = false;
747
748 if (nullptr != clientControlFunction)
749 {
750 std::array<std::uint8_t, CAN_DATA_LENGTH> payload = { 0 };
751 payload[0] = (commandValue & 0x0F);
752 payload[0] |= (elementNumber & 0x0F) << 4;
753 payload[1] = static_cast<std::uint8_t>(elementNumber >> 4);
754 payload[2] = static_cast<std::uint8_t>(dataDescriptionIndex);
755 payload[3] = static_cast<std::uint8_t>(dataDescriptionIndex >> 8);
756 payload[4] = static_cast<std::uint8_t>(processDataValue);
757 payload[5] = static_cast<std::uint8_t>(processDataValue >> 8);
758 payload[6] = static_cast<std::uint8_t>(processDataValue >> 16);
759 payload[7] = static_cast<std::uint8_t>(processDataValue >> 24);
760
761 retVal = send_process_data_to_client(clientControlFunction,
762 payload.data(),
763 payload.size(),
765 }
766 return retVal;
767 }
768
770 {
771 std::array<std::uint8_t, CAN_DATA_LENGTH> payload = {
772 static_cast<std::uint8_t>(ProcessDataCommands::Status) | 0xF0U,
773 0xFF,
774 0xFF,
775 0xFF,
779 0xFF
780 };
781
782 return send_process_data_to_client(nullptr,
783 payload.data(),
784 payload.size(),
786 }
787
788 bool TaskControllerServer::send_version(std::shared_ptr<ControlFunction> clientControlFunction) const
789 {
790 std::array<std::uint8_t, CAN_DATA_LENGTH> payload = {
791 static_cast<std::uint8_t>(TechnicalDataCommandParameters::ParameterVersion) << 4,
792 static_cast<std::uint8_t>(reportedVersion),
793 0xFF,
795 0x00,
799 };
800 return send_process_data_to_client(clientControlFunction,
801 payload.data(),
802 payload.size());
803 }
804
805 std::shared_ptr<TaskControllerServer::ActiveClient> TaskControllerServer::get_active_client(std::shared_ptr<ControlFunction> clientControlFunction) const
806 {
807 for (const auto &activeClient : activeClients)
808 {
809 if ((nullptr != activeClient) &&
810 (nullptr != activeClient->clientControlFunction) &&
811 (nullptr != clientControlFunction) &&
812 (activeClient->clientControlFunction->get_NAME() == clientControlFunction->get_NAME()) &&
813 (activeClient->clientControlFunction->get_can_port() == clientControlFunction->get_can_port()))
814 {
815 return activeClient;
816 }
817 }
818 return nullptr;
819 }
820
821 bool TaskControllerServer::nack_process_data_command(std::shared_ptr<ControlFunction> clientControlFunction) const
822 {
823 bool retVal = false;
824
825 if (nullptr != clientControlFunction)
826 {
827 const std::array<std::uint8_t, CAN_DATA_LENGTH> payload = {
828 static_cast<std::uint8_t>(AcknowledgementType::Negative),
829 0xFF,
830 0xFF,
831 0xFF,
832 clientControlFunction->get_address(),
833 static_cast<std::uint8_t>(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ProcessData) & 0xFF),
834 static_cast<std::uint8_t>((static_cast<std::uint32_t>(CANLibParameterGroupNumber::ProcessData) >> 8) & 0xFF),
835 static_cast<std::uint8_t>((static_cast<std::uint32_t>(CANLibParameterGroupNumber::ProcessData) >> 16) & 0xFF)
836 };
837
838 LOG_WARNING("[TC Server]: NACKing process data command from 0x%02X because they are not known to us. Clients must send the working set master message first.", clientControlFunction->get_address());
839 retVal = CANNetworkManager::CANNetwork.send_can_message(static_cast<std::uint32_t>(CANLibParameterGroupNumber::Acknowledge),
840 payload.data(),
841 payload.size(),
843 clientControlFunction);
844 }
845 return retVal;
846 }
847
848 bool TaskControllerServer::send_structure_label(std::shared_ptr<ControlFunction> clientControlFunction, std::vector<std::uint8_t> &structureLabel, const std::vector<std::uint8_t> &extendedStructureLabel) const
849 {
850 bool retVal = false;
851
852 if (nullptr != clientControlFunction)
853 {
854 while (structureLabel.size() < CAN_DATA_LENGTH - 1)
855 {
856 structureLabel.push_back(0xFF);
857 }
858
859 std::vector<std::uint8_t> payload;
860 payload.push_back(static_cast<std::uint8_t>(ProcessDataCommands::DeviceDescriptor) | static_cast<std::uint8_t>(DeviceDescriptorCommandParameters::StructureLabel) << 4);
861
862 for (const auto &labelByte : structureLabel)
863 {
864 payload.push_back(labelByte);
865 }
866 for (const auto &extendedLabelByte : extendedStructureLabel)
867 {
868 payload.push_back(extendedLabelByte);
869 }
870 retVal = send_process_data_to_client(clientControlFunction,
871 payload.data(),
872 payload.size());
873 }
874 return retVal;
875 }
876
877 bool TaskControllerServer::send_localization_label(std::shared_ptr<ControlFunction> clientControlFunction, const std::array<std::uint8_t, 7> &localizationLabel) const
878 {
879 bool retVal = false;
880
881 if (nullptr != clientControlFunction)
882 {
883 const std::array<std::uint8_t, CAN_DATA_LENGTH> payload = {
884 static_cast<std::uint8_t>(ProcessDataCommands::DeviceDescriptor) | static_cast<std::uint8_t>(DeviceDescriptorCommandParameters::LocalizationLabel) << 4,
885 localizationLabel[0],
886 localizationLabel[1],
887 localizationLabel[2],
888 localizationLabel[3],
889 localizationLabel[4],
890 localizationLabel[5],
891 localizationLabel[6]
892 };
893
894 retVal = send_process_data_to_client(clientControlFunction,
895 payload.data(),
896 payload.size());
897 }
898 return retVal;
899 }
900
901 bool TaskControllerServer::send_request_object_pool_transfer_response(std::shared_ptr<ControlFunction> clientControlFunction, bool isEnoughMemory) const
902 {
903 bool retVal = false;
904
905 if (nullptr != clientControlFunction)
906 {
907 const std::array<std::uint8_t, CAN_DATA_LENGTH> payload = {
909 static_cast<std::uint8_t>(false == isEnoughMemory),
910 0xFF,
911 0xFF,
912 0xFF,
913 0xFF,
914 0xFF,
915 0xFF
916 };
917
918 retVal = send_process_data_to_client(clientControlFunction,
919 payload.data(),
920 payload.size());
921 }
922 return retVal;
923 }
924
925 bool TaskControllerServer::send_object_pool_transfer_response(std::shared_ptr<ControlFunction> clientControlFunction, std::uint8_t errorBitfield, std::uint32_t sizeBytes) const
926 {
927 bool retVal = false;
928
929 if (nullptr != clientControlFunction)
930 {
931 const std::array<std::uint8_t, CAN_DATA_LENGTH> payload = {
932 static_cast<std::uint8_t>(ProcessDataCommands::DeviceDescriptor) | static_cast<std::uint8_t>(DeviceDescriptorCommandParameters::ObjectPoolTransferResponse) << 4,
933 errorBitfield,
934 static_cast<std::uint8_t>(sizeBytes),
935 static_cast<std::uint8_t>(sizeBytes >> 8),
936 static_cast<std::uint8_t>(sizeBytes >> 16),
937 static_cast<std::uint8_t>(sizeBytes >> 24),
938 0xFF,
939 0xFF
940 };
941
942 retVal = send_process_data_to_client(clientControlFunction,
943 payload.data(),
944 payload.size());
945 }
946 return retVal;
947 }
948
949 bool TaskControllerServer::send_object_pool_activate_deactivate_response(std::shared_ptr<ControlFunction> clientControlFunction,
950 std::uint8_t activationErrorBitfield,
951 std::uint8_t objectPoolErrorBitfield,
952 std::uint16_t parentOfFaultingObject,
953 std::uint16_t faultingObject) const
954 {
955 bool retVal = false;
956
957 if (nullptr != clientControlFunction)
958 {
959 const std::array<std::uint8_t, CAN_DATA_LENGTH> payload = {
961 activationErrorBitfield,
962 static_cast<std::uint8_t>(parentOfFaultingObject),
963 static_cast<std::uint8_t>(parentOfFaultingObject >> 8),
964 static_cast<std::uint8_t>(faultingObject),
965 static_cast<std::uint8_t>(faultingObject >> 8),
966 objectPoolErrorBitfield,
967 0xFF
968 };
969 retVal = send_process_data_to_client(clientControlFunction,
970 payload.data(),
971 payload.size());
972 }
973 return retVal;
974 }
975
976 bool TaskControllerServer::send_delete_object_pool_response(std::shared_ptr<ControlFunction> clientControlFunction, bool deletionResult, std::uint8_t errorCode) const
977 {
978 bool retVal = false;
979
980 if (nullptr != clientControlFunction)
981 {
982 const std::array<std::uint8_t, CAN_DATA_LENGTH> payload = {
983 static_cast<std::uint8_t>(ProcessDataCommands::DeviceDescriptor) | static_cast<std::uint8_t>(DeviceDescriptorCommandParameters::DeleteObjectPoolResponse) << 4,
984 static_cast<std::uint8_t>(!deletionResult), // 0 = No errors, 1 = Error
985 errorCode,
986 0xFF,
987 0xFF,
988 0xFF,
989 0xFF,
990 0xFF
991 };
992 retVal = send_process_data_to_client(clientControlFunction,
993 payload.data(),
994 payload.size());
995 }
996 return retVal;
997 }
998
999 bool TaskControllerServer::send_change_designator_response(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t objectID, std::uint8_t errorCode) const
1000 {
1001 bool retVal = false;
1002
1003 if (nullptr != clientControlFunction)
1004 {
1005 const std::array<std::uint8_t, CAN_DATA_LENGTH> payload = {
1006 static_cast<std::uint8_t>(ProcessDataCommands::DeviceDescriptor) | static_cast<std::uint8_t>(DeviceDescriptorCommandParameters::ChangeDesignatorResponse) << 4,
1007 static_cast<std::uint8_t>(objectID),
1008 static_cast<std::uint8_t>(objectID >> 8),
1009 errorCode,
1010 0xFF,
1011 0xFF,
1012 0xFF,
1013 0xFF
1014 };
1015 retVal = send_process_data_to_client(clientControlFunction,
1016 payload.data(),
1017 payload.size());
1018 }
1019 return retVal;
1020 }
1021
1022 bool TaskControllerServer::send_process_data_acknowledge(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint8_t errorBitfield, ProcessDataCommands processDataCommand) const
1023 {
1024 bool retVal = false;
1025
1026 if (nullptr != clientControlFunction)
1027 {
1028 const std::array<std::uint8_t, CAN_DATA_LENGTH> payload = {
1029 static_cast<std::uint8_t>(static_cast<std::uint8_t>(ProcessDataCommands::Acknowledge) | (static_cast<std::uint8_t>(elementNumber & 0x0F) << 4)),
1030 static_cast<std::uint8_t>(elementNumber >> 4),
1031 static_cast<std::uint8_t>(dataDescriptionIndex),
1032 static_cast<std::uint8_t>(dataDescriptionIndex >> 8),
1033 errorBitfield,
1034 static_cast<std::uint8_t>(0xF0 | static_cast<std::uint8_t>(processDataCommand)),
1035 0xFF,
1036 0xFF
1037 };
1038 retVal = send_process_data_to_client(clientControlFunction,
1039 payload.data(),
1040 payload.size(),
1042 }
1043 return retVal;
1044 }
1045
1046 bool TaskControllerServer::send_process_data_to_client(std::shared_ptr<ControlFunction> clientControlFunction,
1047 const std::uint8_t *dataBuffer,
1048 std::uint32_t dataLength,
1049 CANIdentifier::CANPriority priority) const
1050 {
1051 bool retVal = false;
1052
1053 if ((nullptr != dataBuffer) && (dataLength > 0))
1054 {
1055 retVal = CANNetworkManager::CANNetwork.send_can_message(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ProcessData),
1056 dataBuffer,
1057 dataLength,
1059 clientControlFunction,
1060 priority);
1061 }
1062 return retVal;
1063 }
1064}
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...
CANPriority
Defines all the CAN frame priorities that can be encoded in a frame ID.
@ Priority3
Priority highest - 3 (Control messages priority)
@ Priority4
Priority highest - 4.
@ Priority5
Priority highest - 5.
A class that represents a generic CAN message of arbitrary length.
static const std::uint32_t ABSOLUTE_MAX_MESSAGE_LENGTH
ISO11783-3 defines this: The maximum number of packets that can be sent in a single connection with e...
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...
An interface for requesting and parsing the ISO11783 language command PGN, 0xFE0F.
A helper class to assign TC server options. You can use this by doing something like this: TaskContro...
An ISO11783-10 task controller (or data logger) server. A task controller collects task data from con...
bool get_task_totals_active() const
Returns whether a task is currently active or not.
bool send_minimum_threshold_measurement_command(std::shared_ptr< ControlFunction > clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t minimum) const
Sends a minimum threshold measurement command. The process data value for this command is the minimum...
std::uint32_t lastStatusMessageTimestamp_ms
The timestamp of the last status message sent on the bus.
virtual bool on_value_command(std::shared_ptr< ControlFunction > clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::int32_t processDataValue, std::uint8_t &errorCodes)=0
This function will be called by the server when a client sends a value command to the TC....
TaskControllerVersion
Enumerates the different versions of the task controller standard.
virtual ~TaskControllerServer()
Destructor for a TC server.
virtual void identify_task_controller(std::uint8_t taskControllerNumber)=0
This function will be called if someone requests that the TC identify itself. If this gets called,...
std::condition_variable updateWakeupCondition
A condition variable you can optionally use to update the interface when messages are received.
const TaskControllerVersion reportedVersion
The version of the TC that will be reported to the clients.
std::shared_ptr< ActiveClient > get_active_client(std::shared_ptr< ControlFunction > clientControlFunction) const
Checks to see if we are communicating with a control function that is already in our list of active c...
TechnicalDataCommandParameters
Enumerates the subcommands for determining the technical capabilities of a TC, DL,...
@ ParameterVersion
The Request Version message allows the TC, DL, and the client to determine the ISO 11783-10 version o...
@ IdentifyTaskController
The Version message is sent in response to the request version message and contains the ISO 11783-10 ...
bool send_object_pool_transfer_response(std::shared_ptr< ControlFunction > clientControlFunction, std::uint8_t errorBitfield, std::uint32_t sizeBytes) const
Sends a response to an object pool transfer.
void terminate()
Shuts down the TC server, unregisters PGN callbacks.
bool send_request_object_pool_transfer_response(std::shared_ptr< ControlFunction > clientControlFunction, bool isEnoughMemory) const
Sends a response to a request object pool transfer command.
bool send_set_value(std::shared_ptr< ControlFunction > clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t processDataValue) const
Sends a set value command without requesting an acknowledgement. This command is used to set the valu...
bool send_distance_interval_measurement_command(std::shared_ptr< ControlFunction > clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t distanceInterval) const
Sends a distance interval measurement command. The process data value for this command is the distanc...
void process_rx_messages()
Processes messages received from task controller clients.
bool send_change_threshold_measurement_command(std::shared_ptr< ControlFunction > clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t threshold) const
Sends a change threshold measurement command. The process data value for this command is the change t...
void initialize()
Initializes the task controller server.
virtual bool change_designator(std::shared_ptr< ControlFunction > clientControlFunction, std::uint16_t objectIDToAlter, const std::vector< std::uint8_t > &designator)=0
This function will be called by the server when the client wants to change the designator of an objec...
ProcessDataCommands
Enumerates the different process data commands that can be sent to the client.
@ MeasurementMaximumWithinThreshold
The client has to send the value of this data element to the TC or DL when the value is lower than th...
@ MeasurementMinimumWithinThreshold
The client has to send the value of this data element to the TC or DL when the value is higher than t...
@ DeviceDescriptor
Transfer and management of device descriptors.
@ Value
This command is used both to answer a request value command and to set the value of a process data en...
@ PeerControlAssignment
This message is used to establish a connection between a setpoint value source and a setpoint value u...
@ TechnicalCapabilities
Used for determining the technical capabilities of a TC, DL, or client.
@ MeasurementChangeThreshold
The client has to send the value of this data element to the TC or DL when the value change is higher...
@ RequestValue
Used when the value of the data entity specified by the data dictionary identifier is requested.
@ MeasurementTimeInterval
The process data value is the time interval for sending the data element specified by the data dictio...
@ MeasurementDistanceInterval
The process data value is the distance interval for sending the data element specified by the data di...
@ SetValueAndAcknowledge
This command is used to set the value of a process data entity and request a reception acknowledgemen...
@ ClientTask
Sent by the client as a status message every 2s.
@ Acknowledge
Message is a Process Data Acknowledge (PDACK).
@ Status
Message is a Task Controller Status message.
std::mutex messagesMutex
A mutex used to protect the rxMessageQueue.
std::uint8_t currentCommandSourceAddress
The current command source address to send in the status message.
std::deque< CANMessage > rxMessageQueue
A queue of messages received from the clients which will be processed when update is called.
bool send_status_message() const
Sends a status message broadcast.
virtual bool get_is_enough_memory_available(std::uint32_t numberBytesRequired)=0
This function will be called by the server when the client wants to transfer its DDOP to the server a...
static void store_rx_message(const CANMessage &message, void *parentPointer)
Stores messages received from task controller clients for processing later.
void update()
This must be called periodically for the interface to operate correctly.
ObjectPoolErrorCodes
Enumerates the different error codes that can be returned when processing a DDOP.
const std::uint8_t numberChannelsSupportedForPositionBasedControlToReport
The number of channels that will be reported as supported by the TC.
virtual bool get_is_stored_device_descriptor_object_pool_by_localization_label(std::shared_ptr< ControlFunction > clientControlFunction, const std::array< std::uint8_t, 7 > &localizationLabel)=0
This function will be called by the server when the server needs to know if it has previously saved t...
LanguageCommandInterface & get_language_command_interface()
Returns the language command interface used to communicate with the client which language/units are i...
virtual bool get_is_stored_device_descriptor_object_pool_by_structure_label(std::shared_ptr< ControlFunction > clientControlFunction, const std::vector< std::uint8_t > &structureLabel, const std::vector< std::uint8_t > &extendedStructureLabel)=0
This function will be called by the server when the server needs to know if it has previously saved t...
const std::uint8_t numberSectionsSupportedToReport
The number of sections that will be reported as supported by the TC.
void set_task_totals_active(bool isTaskActive)
Use this to set the reported task state in the status message. Basically, this should be set to true ...
bool send_localization_label(std::shared_ptr< ControlFunction > clientControlFunction, const std::array< std::uint8_t, 7 > &localizationLabel) const
Sends a response to a request localization label command.
bool send_maximum_threshold_measurement_command(std::shared_ptr< ControlFunction > clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t maximum) const
Sends a maximum threshold measurement command. The process data value for this command is the maximum...
const std::uint8_t numberBoomsSupportedToReport
The number of booms that will be reported as supported by the TC.
bool send_generic_process_data_default_payload(std::uint8_t multiplexer, std::shared_ptr< ControlFunction > destination) const
This sends a process data message with all FFs in the payload except for the command byte....
bool send_change_designator_response(std::shared_ptr< ControlFunction > clientControlFunction, std::uint16_t objectID, std::uint8_t errorCode) const
Sends a response to a change designator command.
virtual bool store_device_descriptor_object_pool(std::shared_ptr< ControlFunction > clientControlFunction, const std::vector< std::uint8_t > &objectPoolData, bool appendToPool)=0
This function is called when the server wants you to save a DDOP to non volatile memory (NVM)....
LanguageCommandInterface languageCommandInterface
The language command interface used to communicate with the client which language/units are in use.
virtual bool deactivate_object_pool(std::shared_ptr< ControlFunction > clientControlFunction)=0
This function will be called by the server when the client wants to deactivate its DDOP....
bool send_version(std::shared_ptr< ControlFunction > clientControlFunction) const
Sends the version message to a client.
std::deque< std::shared_ptr< ActiveClient > > activeClients
A list of clients that are currently being communicated with.
bool send_object_pool_activate_deactivate_response(std::shared_ptr< ControlFunction > clientControlFunction, std::uint8_t activationErrorBitfield, std::uint8_t objectPoolErrorBitfield, std::uint16_t parentOfFaultingObject, std::uint16_t faultingObject) const
Sends a response to an object pool activate/deactivate command.
bool send_structure_label(std::shared_ptr< ControlFunction > clientControlFunction, std::vector< std::uint8_t > &structureLabel, const std::vector< std::uint8_t > &extendedStructureLabel) const
Sends a response to a request structure label command.
bool get_initialized() const
Returns whether or not the task controller server has been initialized.
TaskControllerServer(std::shared_ptr< InternalControlFunction > internalControlFunction, std::uint8_t numberBoomsSupported, std::uint8_t numberSectionsSupported, std::uint8_t numberChannelsSupportedForPositionBasedControl, const TaskControllerOptions &options, TaskControllerVersion versionToReport=TaskControllerVersion::SecondPublishedEdition)
Constructor for a TC server.
const std::uint8_t optionsBitfieldToReport
The options bitfield that will be reported as supported by the TC.
bool initialized
Whether or not the task controller server has been initialized.
std::condition_variable & get_condition_variable()
Returns a condition variable which you can optionally use to wake up your server's thread when messag...
bool send_process_data_acknowledge(std::shared_ptr< ControlFunction > clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint8_t errorBitfield, ProcessDataCommands processDataCommand) const
Sends a process data acknowledge message to the client.
bool send_set_value_and_acknowledge(std::shared_ptr< ControlFunction > clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t processDataValue) const
Sends a set value and acknowledge command. This command is used to set the value of a process data en...
bool send_request_value(std::shared_ptr< ControlFunction > clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber) const
Sends a request to a client for an element's value of a particular DDI.
static constexpr std::uint32_t STATUS_MESSAGE_RATE_MS
The rate at which status messages are sent to the clients in milliseconds.
DeviceDescriptorCommandParameters
Enumerates subcommands for the transfer and management of device descriptors. These device descriptor...
@ ObjectPoolTransferResponse
Enables the client to transfer (part of) the device descriptor object pool to the TC.
@ LocalizationLabel
Allows the client to determine the availability of the requested device descriptor localization.
@ ObjectPoolTransfer
Sent in response to Request Object-pool Transfer message.
@ DeleteObjectPoolResponse
This is a message to delete the device descriptor object pool for the client that sends this message.
@ DeleteObjectPool
Sent by a client to complete its connection procedure to a TC or DL or to disconnect from a TC or DL.
@ RequestLocalizationLabel
The Structure Label message is sent by the TC or DL to inform the client about the availability of th...
@ RequestObjectPoolTransfer
/// Sent by the TC or DL to inform the client about the availability of the requested localization ve...
@ RequestObjectPoolTransferResponse
/// The Request Object-pool Transfer message allows the client to determine whether it is allowed to ...
@ ObjectPoolActivateDeactivateResponse
Sent by a client to complete its connection procedure to a TC or DL or to disconnect from a TC or DL.
@ StructureLabel
Allows the client to determine the availability of the requested device descriptor structure.
@ ChangeDesignator
TC response to a Object-pool Delete message.
@ ObjectPoolActivateDeactivate
Response to an object pool transfer message.
@ ChangeDesignatorResponse
This message is used to update the designator of an object.
ObjectPoolDeletionErrors
Enumerates the different error codes that can be returned when deleting a device descriptor object po...
bool send_time_interval_measurement_command(std::shared_ptr< ControlFunction > clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t timeInterval) const
Sends a time interval measurement command. The process data value for this command is the time interv...
std::shared_ptr< InternalControlFunction > serverControlFunction
The control function used to communicate with the clients.
virtual bool delete_device_descriptor_object_pool(std::shared_ptr< ControlFunction > clientControlFunction, ObjectPoolDeletionErrors &returnedErrorCode)=0
This function will be called by the server when the client wants to delete its DDOP....
bool send_delete_object_pool_response(std::shared_ptr< ControlFunction > clientControlFunction, bool deletionResult, std::uint8_t errorCode) const
Sends a response to a delete object pool command.
virtual void on_process_data_acknowledge(std::shared_ptr< ControlFunction > clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint8_t errorCodesFromClient, ProcessDataCommands processDataCommand)=0
This function will be called by the server when a client sends an acknowledgement for a process data ...
bool send_process_data_to_client(std::shared_ptr< ControlFunction > clientControlFunction, const std::uint8_t *dataBuffer, std::uint32_t dataLength, CANIdentifier::CANPriority priority=CANIdentifier::CANPriority::Priority5) const
Sends a process data message to a client with a slightly shorter signature than calling send_can_mess...
std::uint8_t currentCommandByte
The current command byte to send in the status message.
ObjectPoolActivationError
Enumerates the different error codes that can be returned when activating a device descriptor object ...
bool nack_process_data_command(std::shared_ptr< ControlFunction > clientControlFunction) const
Sends a negative acknowledge for a the process data PGN which indicates to clients that we aren't lis...
virtual bool activate_object_pool(std::shared_ptr< ControlFunction > clientControlFunction, ObjectPoolActivationError &activationError, ObjectPoolErrorCodes &objectPoolError, std::uint16_t &parentObjectIDOfFaultyObject, std::uint16_t &faultyObjectID)=0
This function will be called by the server when the client wants to activate its DDOP....
bool send_measurement_command(std::shared_ptr< ControlFunction > clientControlFunction, std::uint8_t commandValue, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t processDataValue) const
Sends a measurement command to the client.
std::uint8_t currentStatusByte
The current status byte to send in the status message.
An abstract task controller server class. You can consume this file and implement the pure virtual fu...
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.
@ Negative
"NACK" Indicates the request was not completed or we do not support the PGN