AgIsoStack++
A control-function-focused implementation of the major ISOBUS and J1939 protocols
Loading...
Searching...
No Matches
isobus_virtual_terminal_client.cpp
Go to the documentation of this file.
1//================================================================================================
8//================================================================================================
9
14#include "isobus/utility/platform_endianness.hpp"
15#include "isobus/utility/system_timing.hpp"
16#include "isobus/utility/to_string.hpp"
17
18#include <algorithm>
19#include <cassert>
20#include <cstring>
21#include <functional>
22#include <limits>
23#include <map>
24#include <unordered_map>
25
26namespace isobus
27{
28 VirtualTerminalClient::VirtualTerminalClient(std::shared_ptr<PartneredControlFunction> partner, std::shared_ptr<InternalControlFunction> clientSource) :
29 languageCommandInterface(clientSource, partner),
30 partnerControlFunction(partner),
31 myControlFunction(clientSource),
32 txFlags(static_cast<std::uint32_t>(TransmitFlags::NumberFlags), process_flags, this)
33 {
34 }
35
40
41 void VirtualTerminalClient::initialize(bool spawnThread)
42 {
44 {
45 shouldTerminate = false;
46 initialized = false;
47 }
48
49 if (!initialized)
50 {
51 if (nullptr != partnerControlFunction)
52 {
53 partnerControlFunction->add_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::VirtualTerminalToECU), process_rx_message, this);
54 partnerControlFunction->add_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::Acknowledge), process_rx_message, this);
55 CANNetworkManager::CANNetwork.add_global_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::VirtualTerminalToECU), process_rx_message, this);
56 CANNetworkManager::CANNetwork.add_global_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ECUtoVirtualTerminal), process_rx_message, this);
57 }
58
60 {
62 }
63#if !defined CAN_STACK_DISABLE_THREADS && !defined ARDUINO
64 if (spawnThread)
65 {
66 workerThread = new std::thread([this]() { worker_thread_function(); });
67 }
68#endif
69 initialized = true;
70 }
71 }
72
74 {
75 return initialized;
76 }
77
82
84 {
85 if (initialized)
86 {
87 if (nullptr != partnerControlFunction)
88 {
91 {
92 LOG_DEBUG("[VT]: Requested object pool deletion from volatile VT memory.");
93 }
94 partnerControlFunction->remove_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::VirtualTerminalToECU), process_rx_message, this);
95 partnerControlFunction->remove_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::Acknowledge), process_rx_message, this);
96 CANNetworkManager::CANNetwork.remove_global_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::VirtualTerminalToECU), process_rx_message, this);
97 CANNetworkManager::CANNetwork.remove_global_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ECUtoVirtualTerminal), process_rx_message, this);
98 }
99
100 shouldTerminate = true;
101#if !defined CAN_STACK_DISABLE_THREADS && !defined ARDUINO
102 if (nullptr != workerThread)
103 {
104 workerThread->join();
105 delete workerThread;
106 workerThread = nullptr;
107 }
108#endif
109 initialized = false;
111 LOG_INFO("[VT]: VT Client connection has been terminated.");
112 }
113 }
114
116 {
117 LOG_INFO("[VT]:VT Client connection restart requested. Client will now terminate and reinitialize.");
118#if !defined CAN_STACK_DISABLE_THREADS && !defined ARDUINO
119 bool workerNeeded = (nullptr != workerThread);
120#else
121 bool workerNeeded = false;
122#endif
123 terminate();
124 initialize(workerNeeded);
125 }
126
127 std::shared_ptr<PartneredControlFunction> VirtualTerminalClient::get_partner_control_function() const
128 {
130 }
131
132 std::shared_ptr<InternalControlFunction> VirtualTerminalClient::get_internal_control_function() const
133 {
134 return myControlFunction;
135 }
136
138 {
139 std::uint8_t retVal = NULL_CAN_ADDRESS;
140
141 if (get_is_connected())
142 {
144 }
145 return retVal;
146 }
147
148 EventDispatcher<VirtualTerminalClient::VTKeyEvent> &VirtualTerminalClient::get_vt_soft_key_event_dispatcher()
149 {
151 }
152
153 EventDispatcher<VirtualTerminalClient::VTKeyEvent> &VirtualTerminalClient::get_vt_button_event_dispatcher()
154 {
156 }
157
158 EventDispatcher<VirtualTerminalClient::VTPointingEvent> &VirtualTerminalClient::get_vt_pointing_event_dispatcher()
159 {
161 }
162
163 EventDispatcher<VirtualTerminalClient::VTSelectInputObjectEvent> &VirtualTerminalClient::get_vt_select_input_object_event_dispatcher()
164 {
166 }
167
168 EventDispatcher<VirtualTerminalClient::VTESCMessageEvent> &VirtualTerminalClient::get_vt_esc_message_event_dispatcher()
169 {
171 }
172
173 EventDispatcher<VirtualTerminalClient::VTChangeNumericValueEvent> &VirtualTerminalClient::get_vt_change_numeric_value_event_dispatcher()
174 {
176 }
177
178 EventDispatcher<VirtualTerminalClient::VTChangeActiveMaskEvent> &VirtualTerminalClient::get_vt_change_active_mask_event_dispatcher()
179 {
181 }
182
183 EventDispatcher<VirtualTerminalClient::VTChangeSoftKeyMaskEvent> &VirtualTerminalClient::get_vt_change_soft_key_mask_event_dispatcher()
184 {
186 }
187
188 EventDispatcher<VirtualTerminalClient::VTChangeStringValueEvent> &VirtualTerminalClient::get_vt_change_string_value_event_dispatcher()
189 {
191 }
192
193 EventDispatcher<VirtualTerminalClient::VTUserLayoutHideShowEvent> &VirtualTerminalClient::get_vt_user_layout_hide_show_event_dispatcher()
194 {
196 }
197
198 EventDispatcher<VirtualTerminalClient::VTAudioSignalTerminationEvent> &VirtualTerminalClient::get_vt_control_audio_signal_termination_event_dispatcher()
199 {
201 }
202
203 EventDispatcher<VirtualTerminalClient::AuxiliaryFunctionEvent> &VirtualTerminalClient::get_auxiliary_function_event_dispatcher()
204 {
206 }
207
209 {
210 ourModelIdentificationCode = modelIdentificationCode;
211 }
212
214 {
215 return 0x40 == (busyCodesBitfield & 0x40);
216 }
217
218 void VirtualTerminalClient::add_auxiliary_input_object_id(const std::uint16_t auxiliaryInputID)
219 {
220 ourAuxiliaryInputs[auxiliaryInputID] = AuxiliaryInputState{ 0, false, false, false, 0, 0 };
221 }
222
223 void VirtualTerminalClient::remove_auxiliary_input_object_id(const std::uint16_t auxiliaryInputID)
224 {
225 if (ourAuxiliaryInputs.count(auxiliaryInputID))
226 {
227 ourAuxiliaryInputs.erase(auxiliaryInputID);
228 LOG_DEBUG("[AUX-N] Removed auxiliary input with ID: " +
229 isobus::to_string(static_cast<int>(auxiliaryInputID)));
230 }
231 }
232
233 void VirtualTerminalClient::update_auxiliary_input(const std::uint16_t auxiliaryInputID, const std::uint16_t value1, const std::uint16_t value2, const bool controlLocked)
234 {
235 if (!ourAuxiliaryInputs.count(auxiliaryInputID))
236 {
237 LOG_WARNING("[AUX-N] Auxiliary input with ID '" +
238 isobus::to_string(static_cast<int>(auxiliaryInputID)) +
239 "' has not been registered. Ignoring update");
240 return;
241 }
242
244 {
245 if ((value1 != ourAuxiliaryInputs.at(auxiliaryInputID).value1) || (value2 != ourAuxiliaryInputs.at(auxiliaryInputID).value2))
246 {
247 ourAuxiliaryInputs.at(auxiliaryInputID).value1 = value1;
248 ourAuxiliaryInputs.at(auxiliaryInputID).value2 = value2;
249 ourAuxiliaryInputs.at(auxiliaryInputID).controlLocked = controlLocked;
250 ourAuxiliaryInputs.at(auxiliaryInputID).hasInteraction = true;
251 update_auxiliary_input_status(auxiliaryInputID);
252 }
253 }
254 }
255
256 isobus::VirtualTerminalClient::AssignedAuxiliaryFunction::AssignedAuxiliaryFunction(const std::uint16_t functionObjectID, const std::uint16_t inputObjectID, const AuxiliaryTypeTwoFunctionType functionType) :
257 functionObjectID(functionObjectID), inputObjectID(inputObjectID), functionType(functionType)
258 {
259 }
260
262 {
263 return (functionObjectID == other.functionObjectID) && (inputObjectID == other.inputObjectID) && (functionType == other.functionType);
264 }
265
267 {
268 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::HideShowObjectCommand),
269 static_cast<std::uint8_t>(objectID & 0xFF),
270 static_cast<std::uint8_t>(objectID >> 8),
271 static_cast<std::uint8_t>(command),
272 0xFF,
273 0xFF,
274 0xFF,
275 0xFF };
276 return queue_command(buffer);
277 }
278
280 {
281 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::EnableDisableObjectCommand),
282 static_cast<std::uint8_t>(objectID & 0xFF),
283 static_cast<std::uint8_t>(objectID >> 8),
284 static_cast<std::uint8_t>(command),
285 0xFF,
286 0xFF,
287 0xFF,
288 0xFF };
289 return queue_command(buffer);
290 }
291
293 {
294 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::SelectInputObjectCommand),
295 static_cast<std::uint8_t>(objectID & 0xFF),
296 static_cast<std::uint8_t>(objectID >> 8),
297 static_cast<std::uint8_t>(option),
298 0xFF,
299 0xFF,
300 0xFF,
301 0xFF };
302 return queue_command(buffer, true);
303 }
304
306 {
307 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::ESCCommand),
308 0xFF,
309 0xFF,
310 0xFF,
311 0xFF,
312 0xFF,
313 0xFF,
314 0xFF };
315 return queue_command(buffer, true);
316 }
317
318 bool VirtualTerminalClient::send_control_audio_signal(std::uint8_t activations, std::uint16_t frequency_hz, std::uint16_t duration_ms, std::uint16_t offTimeDuration_ms)
319 {
320 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::ControlAudioSignalCommand),
321 activations,
322 static_cast<std::uint8_t>(frequency_hz & 0xFF),
323 static_cast<std::uint8_t>(frequency_hz >> 8),
324 static_cast<std::uint8_t>(duration_ms & 0xFF),
325 static_cast<std::uint8_t>(duration_ms >> 8),
326 static_cast<std::uint8_t>(offTimeDuration_ms & 0xFF),
327 static_cast<std::uint8_t>(offTimeDuration_ms >> 8) };
328 return queue_command(buffer, true);
329 }
330
331 bool VirtualTerminalClient::send_set_audio_volume(std::uint8_t volume_percent)
332 {
333 constexpr std::uint8_t MAX_VOLUME_PERCENT = 100;
334
335 if (volume_percent > MAX_VOLUME_PERCENT)
336 {
337 volume_percent = MAX_VOLUME_PERCENT;
338 LOG_WARNING("[VT]: Cannot try to set audio volume greater than 100 percent. Value will be capped at 100.");
339 }
340
341 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::SetAudioVolumeCommand),
342 volume_percent,
343 0xFF,
344 0xFF,
345 0xFF,
346 0xFF,
347 0xFF,
348 0xFF };
349 return queue_command(buffer, true);
350 }
351
352 bool VirtualTerminalClient::send_change_child_location(std::uint16_t objectID, std::uint16_t parentObjectID, std::uint8_t relativeXPositionChange, std::uint8_t relativeYPositionChange)
353 {
354 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::ChangeChildLocationCommand),
355 static_cast<std::uint8_t>(parentObjectID & 0xFF),
356 static_cast<std::uint8_t>(parentObjectID >> 8),
357 static_cast<std::uint8_t>(objectID & 0xFF),
358 static_cast<std::uint8_t>(objectID >> 8),
359 relativeXPositionChange,
360 relativeYPositionChange,
361 0xFF };
362 return queue_command(buffer);
363 }
364
365 bool VirtualTerminalClient::send_change_child_position(std::uint16_t objectID, std::uint16_t parentObjectID, std::uint16_t xPosition, std::uint16_t yPosition)
366 {
367 const std::vector<std::uint8_t> buffer = {
368 static_cast<std::uint8_t>(Function::ChangeChildPositionCommand),
369 static_cast<std::uint8_t>(parentObjectID & 0xFF),
370 static_cast<std::uint8_t>(parentObjectID >> 8),
371 static_cast<std::uint8_t>(objectID & 0xFF),
372 static_cast<std::uint8_t>(objectID >> 8),
373 static_cast<std::uint8_t>(xPosition & 0xFF),
374 static_cast<std::uint8_t>(xPosition >> 8),
375 static_cast<std::uint8_t>(yPosition & 0xFF),
376 static_cast<std::uint8_t>(yPosition >> 8),
377 };
378 return queue_command(buffer);
379 }
380
381 bool VirtualTerminalClient::send_change_size_command(std::uint16_t objectID, std::uint16_t newWidth, std::uint16_t newHeight)
382 {
383 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::ChangeSizeCommand),
384 static_cast<std::uint8_t>(objectID & 0xFF),
385 static_cast<std::uint8_t>(objectID >> 8),
386 static_cast<std::uint8_t>(newWidth & 0xFF),
387 static_cast<std::uint8_t>(newWidth >> 8),
388 static_cast<std::uint8_t>(newHeight & 0xFF),
389 static_cast<std::uint8_t>(newHeight >> 8),
390 0xFF };
391 return queue_command(buffer);
392 }
393
394 bool VirtualTerminalClient::send_change_background_colour(std::uint16_t objectID, std::uint8_t colour)
395 {
396 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::ChangeBackgroundColourCommand),
397 static_cast<std::uint8_t>(objectID & 0xFF),
398 static_cast<std::uint8_t>(objectID >> 8),
399 colour,
400 0xFF,
401 0xFF,
402 0xFF,
403 0xFF };
404 return queue_command(buffer);
405 }
406
407 bool VirtualTerminalClient::send_change_numeric_value(std::uint16_t objectID, std::uint32_t value)
408 {
409 const std::vector<std::uint8_t> buffer = {
410 static_cast<std::uint8_t>(Function::ChangeNumericValueCommand),
411 static_cast<std::uint8_t>(objectID & 0xFF),
412 static_cast<std::uint8_t>(objectID >> 8),
413 0xFF,
414 static_cast<std::uint8_t>(value & 0xFF),
415 static_cast<std::uint8_t>((value >> 8) & 0xFF),
416 static_cast<std::uint8_t>((value >> 16) & 0xFF),
417 static_cast<std::uint8_t>((value >> 24) & 0xFF),
418 };
419 return queue_command(buffer);
420 }
421
422 bool VirtualTerminalClient::send_change_string_value(std::uint16_t objectID, uint16_t stringLength, const char *value)
423 {
424 bool retVal = false;
425
426 if (nullptr != value)
427 {
428 std::vector<std::uint8_t> buffer;
429 buffer.resize(5 + stringLength);
430 buffer[0] = static_cast<std::uint8_t>(Function::ChangeStringValueCommand);
431 buffer[1] = static_cast<std::uint8_t>(objectID & 0xFF);
432 buffer[2] = static_cast<std::uint8_t>(objectID >> 8);
433 buffer[3] = static_cast<std::uint8_t>(stringLength & 0xFF);
434 buffer[4] = static_cast<std::uint8_t>(stringLength >> 8);
435 for (std::uint16_t i = 0; i < stringLength; i++)
436 {
437 buffer[5 + i] = value[i];
438 }
439
440 while (buffer.size() < CAN_DATA_LENGTH)
441 {
442 buffer.push_back(0xFF); // Pad to minimum length
443 }
444 retVal = queue_command(buffer);
445 }
446 return retVal;
447 }
448
449 bool VirtualTerminalClient::send_change_string_value(std::uint16_t objectID, const std::string &value)
450 {
451 return send_change_string_value(objectID, value.size(), value.c_str());
452 }
453
454 bool VirtualTerminalClient::send_change_endpoint(std::uint16_t objectID, std::uint16_t width_px, std::uint16_t height_px, LineDirection direction)
455 {
456 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::ChangeEndPointCommand),
457 static_cast<std::uint8_t>(objectID & 0xFF),
458 static_cast<std::uint8_t>(objectID >> 8),
459 static_cast<std::uint8_t>(width_px & 0xFF),
460 static_cast<std::uint8_t>(width_px >> 8),
461 static_cast<std::uint8_t>(height_px & 0xFF),
462 static_cast<std::uint8_t>(height_px >> 8),
463 static_cast<std::uint8_t>(direction) };
464 return queue_command(buffer);
465 }
466
467 bool VirtualTerminalClient::send_change_font_attributes(std::uint16_t objectID, std::uint8_t colour, FontSize size, std::uint8_t type, std::uint8_t styleBitfield)
468 {
469 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::ChangeFontAttributesCommand),
470 static_cast<std::uint8_t>(objectID & 0xFF),
471 static_cast<std::uint8_t>(objectID >> 8),
472 colour,
473 static_cast<std::uint8_t>(size),
474 type,
475 styleBitfield,
476 0xFF };
477 return queue_command(buffer);
478 }
479
480 bool VirtualTerminalClient::send_change_line_attributes(std::uint16_t objectID, std::uint8_t colour, std::uint8_t width, std::uint16_t lineArtBitmask)
481 {
482 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::ChangeLineAttributesCommand),
483 static_cast<std::uint8_t>(objectID & 0xFF),
484 static_cast<std::uint8_t>(objectID >> 8),
485 colour,
486 static_cast<std::uint8_t>(width),
487 static_cast<std::uint8_t>(lineArtBitmask & 0xFF),
488 static_cast<std::uint8_t>(lineArtBitmask >> 8),
489 0xFF };
490 return queue_command(buffer);
491 }
492
493 bool VirtualTerminalClient::send_change_fill_attributes(std::uint16_t objectID, FillType fillType, std::uint8_t colour, std::uint16_t fillPatternObjectID)
494 {
495 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::ChangeFillAttributesCommand),
496 static_cast<std::uint8_t>(objectID & 0xFF),
497 static_cast<std::uint8_t>(objectID >> 8),
498 static_cast<std::uint8_t>(fillType),
499 colour,
500 static_cast<std::uint8_t>(fillPatternObjectID & 0xFF),
501 static_cast<std::uint8_t>(fillPatternObjectID >> 8),
502 0xFF };
503 return queue_command(buffer);
504 }
505
506 bool VirtualTerminalClient::send_change_active_mask(std::uint16_t workingSetObjectID, std::uint16_t newActiveMaskObjectID)
507 {
508 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::ChangeActiveMaskCommand),
509 static_cast<std::uint8_t>(workingSetObjectID & 0xFF),
510 static_cast<std::uint8_t>(workingSetObjectID >> 8),
511 static_cast<std::uint8_t>(newActiveMaskObjectID & 0xFF),
512 static_cast<std::uint8_t>(newActiveMaskObjectID >> 8),
513 0xFF,
514 0xFF,
515 0xFF };
516 return queue_command(buffer, true);
517 }
518
519 bool VirtualTerminalClient::send_change_softkey_mask(MaskType type, std::uint16_t dataOrAlarmMaskObjectID, std::uint16_t newSoftKeyMaskObjectID)
520 {
521 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::ChangeSoftKeyMaskCommand),
522 static_cast<std::uint8_t>(type),
523 static_cast<std::uint8_t>(dataOrAlarmMaskObjectID & 0xFF),
524 static_cast<std::uint8_t>(dataOrAlarmMaskObjectID >> 8),
525 static_cast<std::uint8_t>(newSoftKeyMaskObjectID & 0xFF),
526 static_cast<std::uint8_t>(newSoftKeyMaskObjectID >> 8),
527 0xFF,
528 0xFF };
529 return queue_command(buffer, true);
530 }
531
532 bool VirtualTerminalClient::send_change_attribute(std::uint16_t objectID, std::uint8_t attributeID, std::uint32_t value)
533 {
534 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::ChangeAttributeCommand),
535 static_cast<std::uint8_t>(objectID & 0xFF),
536 static_cast<std::uint8_t>(objectID >> 8),
537 attributeID,
538 static_cast<std::uint8_t>(value & 0xFF),
539 static_cast<std::uint8_t>((value >> 8) & 0xFF),
540 static_cast<std::uint8_t>((value >> 16) & 0xFF),
541 static_cast<std::uint8_t>((value >> 24) & 0xFF) };
542 return queue_command(buffer);
543 }
544
545 bool VirtualTerminalClient::send_change_attribute(std::uint16_t objectID, std::uint8_t attributeID, float value)
546 {
547 static_assert(sizeof(float) == 4, "Float must be 4 bytes");
548 std::array<std::uint8_t, sizeof(float)> floatBytes = { 0 };
549 memcpy(floatBytes.data(), &value, sizeof(float));
550
551 if (is_big_endian())
552 {
553 std::reverse(floatBytes.begin(), floatBytes.end());
554 }
555
556 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::ChangeAttributeCommand),
557 static_cast<std::uint8_t>(objectID & 0xFF),
558 static_cast<std::uint8_t>(objectID >> 8),
559 attributeID,
560 floatBytes[0],
561 floatBytes[1],
562 floatBytes[2],
563 floatBytes[3] };
564 return queue_command(buffer);
565 }
566
567 bool VirtualTerminalClient::send_change_priority(std::uint16_t alarmMaskObjectID, AlarmMaskPriority priority)
568 {
569 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::ChangePriorityCommand),
570 static_cast<std::uint8_t>(alarmMaskObjectID & 0xFF),
571 static_cast<std::uint8_t>(alarmMaskObjectID >> 8),
572 static_cast<std::uint8_t>(priority),
573 0xFF,
574 0xFF,
575 0xFF,
576 0xFF };
577 return queue_command(buffer);
578 }
579
580 bool VirtualTerminalClient::send_change_list_item(std::uint16_t objectID, std::uint8_t listIndex, std::uint16_t newObjectID)
581 {
582 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::ChangeListItemCommand),
583 static_cast<std::uint8_t>(objectID & 0xFF),
584 static_cast<std::uint8_t>(objectID >> 8),
585 listIndex,
586 static_cast<std::uint8_t>(newObjectID & 0xFF),
587 static_cast<std::uint8_t>(newObjectID >> 8),
588 0xFF,
589 0xFF };
590 return queue_command(buffer);
591 }
592
593 bool VirtualTerminalClient::send_lock_unlock_mask(MaskLockState state, std::uint16_t objectID, std::uint16_t timeout_ms)
594 {
595 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::LockUnlockMaskCommand),
596 static_cast<std::uint8_t>(state),
597 static_cast<std::uint8_t>(objectID & 0xFF),
598 static_cast<std::uint8_t>(objectID >> 8),
599 static_cast<std::uint8_t>(timeout_ms & 0xFF),
600 static_cast<std::uint8_t>(timeout_ms >> 8),
601 0xFF,
602 0xFF };
603 return queue_command(buffer, true);
604 }
605
606 bool VirtualTerminalClient::send_execute_macro(std::uint16_t objectID)
607 {
608 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::ExecuteMacroCommand),
609 static_cast<std::uint8_t>(objectID & 0xFF),
610 static_cast<std::uint8_t>(objectID >> 8),
611 0xFF,
612 0xFF,
613 0xFF,
614 0xFF,
615 0xFF };
616 return queue_command(buffer);
617 }
618
619 bool VirtualTerminalClient::send_change_object_label(std::uint16_t objectID, std::uint16_t labelStringObjectID, std::uint8_t fontType, std::uint16_t graphicalDesignatorObjectID)
620 {
621 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::ChangeObjectLabelCommand),
622 static_cast<std::uint8_t>(objectID & 0xFF),
623 static_cast<std::uint8_t>(objectID >> 8),
624 static_cast<std::uint8_t>(labelStringObjectID & 0xFF),
625 static_cast<std::uint8_t>(labelStringObjectID >> 8),
626 fontType,
627 static_cast<std::uint8_t>(graphicalDesignatorObjectID & 0xFF),
628 static_cast<std::uint8_t>(graphicalDesignatorObjectID >> 8) };
629 return queue_command(buffer);
630 }
631
632 bool VirtualTerminalClient::send_change_polygon_point(std::uint16_t objectID, std::uint8_t pointIndex, std::uint16_t newXValue, std::uint16_t newYValue)
633 {
634 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::ChangePolygonPointCommand),
635 static_cast<std::uint8_t>(objectID & 0xFF),
636 static_cast<std::uint8_t>(objectID >> 8),
637 pointIndex,
638 static_cast<std::uint8_t>(newXValue & 0xFF),
639 static_cast<std::uint8_t>(newXValue >> 8),
640 static_cast<std::uint8_t>(newYValue & 0xFF),
641 static_cast<std::uint8_t>(newYValue >> 8) };
642 return queue_command(buffer);
643 }
644
645 bool VirtualTerminalClient::send_change_polygon_scale(std::uint16_t objectID, std::uint16_t widthAttribute, std::uint16_t heightAttribute)
646 {
647 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::ChangePolygonScaleCommand),
648 static_cast<std::uint8_t>(objectID & 0xFF),
649 static_cast<std::uint8_t>(objectID >> 8),
650 static_cast<std::uint8_t>(widthAttribute & 0xFF),
651 static_cast<std::uint8_t>(widthAttribute >> 8),
652 static_cast<std::uint8_t>(heightAttribute & 0xFF),
653 static_cast<std::uint8_t>(heightAttribute >> 8),
654 0xFF };
655 return queue_command(buffer);
656 }
657
659 {
660 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::SelectColourMapCommand),
661 static_cast<std::uint8_t>(objectID & 0xFF),
662 static_cast<std::uint8_t>(objectID >> 8),
663 0xFF,
664 0xFF,
665 0xFF,
666 0xFF,
667 0xFF };
668 return queue_command(buffer, true);
669 }
670
672 {
673 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::ExecuteExtendedMacroCommand),
674 static_cast<std::uint8_t>(objectID & 0xFF),
675 static_cast<std::uint8_t>(objectID >> 8),
676 0xFF,
677 0xFF,
678 0xFF,
679 0xFF,
680 0xFF };
681 return queue_command(buffer);
682 }
683
684 bool VirtualTerminalClient::send_select_active_working_set(std::uint64_t NAMEofWorkingSetMasterForDesiredWorkingSet)
685 {
686 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::SelectActiveWorkingSet),
687 static_cast<std::uint8_t>(NAMEofWorkingSetMasterForDesiredWorkingSet & 0xFF),
688 static_cast<std::uint8_t>((NAMEofWorkingSetMasterForDesiredWorkingSet >> 8) & 0xFF),
689 static_cast<std::uint8_t>((NAMEofWorkingSetMasterForDesiredWorkingSet >> 16) & 0xFF),
690 static_cast<std::uint8_t>((NAMEofWorkingSetMasterForDesiredWorkingSet >> 24) & 0xFF),
691 static_cast<std::uint8_t>((NAMEofWorkingSetMasterForDesiredWorkingSet >> 32) & 0xFF),
692 static_cast<std::uint8_t>((NAMEofWorkingSetMasterForDesiredWorkingSet >> 40) & 0xFF),
693 static_cast<std::uint8_t>((NAMEofWorkingSetMasterForDesiredWorkingSet >> 48) & 0xFF),
694 static_cast<std::uint8_t>((NAMEofWorkingSetMasterForDesiredWorkingSet >> 56) & 0xFF) };
695 return queue_command(buffer, true);
696 }
697
698 bool VirtualTerminalClient::send_set_graphics_cursor(std::uint16_t objectID, std::int16_t xPosition, std::int16_t yPosition)
699 {
700 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::GraphicsContextCommand),
701 static_cast<std::uint8_t>(objectID & 0xFF),
702 static_cast<std::uint8_t>(objectID >> 8),
703 static_cast<std::uint8_t>(GraphicsContextSubCommandID::SetGraphicsCursor),
704 static_cast<std::uint8_t>(xPosition & 0xFF),
705 static_cast<std::uint8_t>(xPosition >> 8),
706 static_cast<std::uint8_t>(yPosition & 0xFF),
707 static_cast<std::uint8_t>(yPosition >> 8) };
708 return queue_command(buffer);
709 }
710
711 bool VirtualTerminalClient::send_move_graphics_cursor(std::uint16_t objectID, std::int16_t xOffset, std::int16_t yOffset)
712 {
713 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::GraphicsContextCommand),
714 static_cast<std::uint8_t>(objectID & 0xFF),
715 static_cast<std::uint8_t>(objectID >> 8),
716 static_cast<std::uint8_t>(GraphicsContextSubCommandID::MoveGraphicsCursor),
717 static_cast<std::uint8_t>(xOffset & 0xFF),
718 static_cast<std::uint8_t>(xOffset >> 8),
719 static_cast<std::uint8_t>(yOffset & 0xFF),
720 static_cast<std::uint8_t>(yOffset >> 8) };
721 return queue_command(buffer);
722 }
723
724 bool VirtualTerminalClient::send_set_foreground_colour(std::uint16_t objectID, std::uint8_t colour)
725 {
726 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::GraphicsContextCommand),
727 static_cast<std::uint8_t>(objectID & 0xFF),
728 static_cast<std::uint8_t>(objectID >> 8),
729 static_cast<std::uint8_t>(GraphicsContextSubCommandID::SetForegroundColour),
730 colour,
731 0xFF,
732 0xFF,
733 0xFF };
734 return queue_command(buffer);
735 }
736
737 bool VirtualTerminalClient::send_set_background_colour(std::uint16_t objectID, std::uint8_t colour)
738 {
739 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::GraphicsContextCommand),
740 static_cast<std::uint8_t>(objectID & 0xFF),
741 static_cast<std::uint8_t>(objectID >> 8),
742 static_cast<std::uint8_t>(GraphicsContextSubCommandID::SetBackgroundColour),
743 colour,
744 0xFF,
745 0xFF,
746 0xFF };
747 return queue_command(buffer);
748 }
749
750 bool VirtualTerminalClient::send_set_line_attributes_object_id(std::uint16_t objectID, std::uint16_t lineAttributesObjectID)
751 {
752 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::GraphicsContextCommand),
753 static_cast<std::uint8_t>(objectID & 0xFF),
754 static_cast<std::uint8_t>(objectID >> 8),
756 static_cast<std::uint8_t>(lineAttributesObjectID & 0xFF),
757 static_cast<std::uint8_t>(lineAttributesObjectID >> 8),
758 0xFF,
759 0xFF };
760 return queue_command(buffer);
761 }
762
763 bool VirtualTerminalClient::send_set_fill_attributes_object_id(std::uint16_t objectID, std::uint16_t fillAttributesObjectID)
764 {
765 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::GraphicsContextCommand),
766 static_cast<std::uint8_t>(objectID & 0xFF),
767 static_cast<std::uint8_t>(objectID >> 8),
769 static_cast<std::uint8_t>(fillAttributesObjectID & 0xFF),
770 static_cast<std::uint8_t>(fillAttributesObjectID >> 8),
771 0xFF,
772 0xFF };
773 return queue_command(buffer);
774 }
775
776 bool VirtualTerminalClient::send_set_font_attributes_object_id(std::uint16_t objectID, std::uint16_t fontAttributesObjectID)
777 {
778 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::GraphicsContextCommand),
779 static_cast<std::uint8_t>(objectID & 0xFF),
780 static_cast<std::uint8_t>(objectID >> 8),
782 static_cast<std::uint8_t>(fontAttributesObjectID & 0xFF),
783 static_cast<std::uint8_t>(fontAttributesObjectID >> 8),
784 0xFF,
785 0xFF };
786 return queue_command(buffer);
787 }
788
789 bool VirtualTerminalClient::send_erase_rectangle(std::uint16_t objectID, std::uint16_t width, std::uint16_t height)
790 {
791 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::GraphicsContextCommand),
792 static_cast<std::uint8_t>(objectID & 0xFF),
793 static_cast<std::uint8_t>(objectID >> 8),
794 static_cast<std::uint8_t>(GraphicsContextSubCommandID::EraseRectangle),
795 static_cast<std::uint8_t>(width & 0xFF),
796 static_cast<std::uint8_t>(width >> 8),
797 static_cast<std::uint8_t>(height & 0xFF),
798 static_cast<std::uint8_t>(height >> 8) };
799 return queue_command(buffer);
800 }
801
802 bool VirtualTerminalClient::send_draw_point(std::uint16_t objectID, std::int16_t xOffset, std::int16_t yOffset)
803 {
804 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::GraphicsContextCommand),
805 static_cast<std::uint8_t>(objectID & 0xFF),
806 static_cast<std::uint8_t>(objectID >> 8),
807 static_cast<std::uint8_t>(GraphicsContextSubCommandID::DrawPoint),
808 static_cast<std::uint8_t>(xOffset & 0xFF),
809 static_cast<std::uint8_t>(xOffset >> 8),
810 static_cast<std::uint8_t>(yOffset & 0xFF),
811 static_cast<std::uint8_t>(yOffset >> 8) };
812 return queue_command(buffer);
813 }
814
815 bool VirtualTerminalClient::send_draw_line(std::uint16_t objectID, std::int16_t xOffset, std::int16_t yOffset)
816 {
817 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::GraphicsContextCommand),
818 static_cast<std::uint8_t>(objectID & 0xFF),
819 static_cast<std::uint8_t>(objectID >> 8),
820 static_cast<std::uint8_t>(GraphicsContextSubCommandID::DrawLine),
821 static_cast<std::uint8_t>(xOffset & 0xFF),
822 static_cast<std::uint8_t>(xOffset >> 8),
823 static_cast<std::uint8_t>(yOffset & 0xFF),
824 static_cast<std::uint8_t>(yOffset >> 8) };
825 return queue_command(buffer);
826 }
827
828 bool VirtualTerminalClient::send_draw_rectangle(std::uint16_t objectID, std::uint16_t width, std::uint16_t height)
829 {
830 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::GraphicsContextCommand),
831 static_cast<std::uint8_t>(objectID & 0xFF),
832 static_cast<std::uint8_t>(objectID >> 8),
833 static_cast<std::uint8_t>(GraphicsContextSubCommandID::DrawRectangle),
834 static_cast<std::uint8_t>(width & 0xFF),
835 static_cast<std::uint8_t>(width >> 8),
836 static_cast<std::uint8_t>(height & 0xFF),
837 static_cast<std::uint8_t>(height >> 8) };
838 return queue_command(buffer);
839 }
840
841 bool VirtualTerminalClient::send_draw_closed_ellipse(std::uint16_t objectID, std::uint16_t width, std::uint16_t height)
842 {
843 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::GraphicsContextCommand),
844 static_cast<std::uint8_t>(objectID & 0xFF),
845 static_cast<std::uint8_t>(objectID >> 8),
846 static_cast<std::uint8_t>(GraphicsContextSubCommandID::DrawClosedEllipse),
847 static_cast<std::uint8_t>(width & 0xFF),
848 static_cast<std::uint8_t>(width >> 8),
849 static_cast<std::uint8_t>(height & 0xFF),
850 static_cast<std::uint8_t>(height >> 8) };
851 return queue_command(buffer);
852 }
853
854 bool VirtualTerminalClient::send_draw_polygon(std::uint16_t objectID, std::uint8_t numberOfPoints, const std::int16_t *listOfXOffsetsRelativeToCursor, const std::int16_t *listOfYOffsetsRelativeToCursor)
855 {
856 bool retVal = false;
857
858 if ((numberOfPoints > 0) &&
859 (nullptr != listOfXOffsetsRelativeToCursor) &&
860 (nullptr != listOfYOffsetsRelativeToCursor))
861
862 {
863 const std::uint16_t messageLength = (9 + (4 * numberOfPoints));
864 std::vector<std::uint8_t> buffer;
865 buffer.resize(messageLength);
866 buffer[0] = static_cast<std::uint8_t>(Function::GraphicsContextCommand);
867 buffer[1] = static_cast<std::uint8_t>(objectID & 0xFF);
868 buffer[2] = static_cast<std::uint8_t>(objectID >> 8);
869 buffer[3] = static_cast<std::uint8_t>(GraphicsContextSubCommandID::DrawPolygon);
870 buffer[4] = numberOfPoints;
871 for (std::uint16_t i = 0; i < numberOfPoints; i += 4)
872 {
873 buffer[5 + i] = static_cast<std::uint8_t>(listOfXOffsetsRelativeToCursor[0] & 0xFF);
874 buffer[6 + i] = static_cast<std::uint8_t>((listOfXOffsetsRelativeToCursor[0] >> 8) & 0xFF);
875 buffer[7 + i] = static_cast<std::uint8_t>(listOfYOffsetsRelativeToCursor[0] & 0xFF);
876 buffer[8 + i] = static_cast<std::uint8_t>((listOfYOffsetsRelativeToCursor[0] >> 8) & 0xFF);
877 }
878 retVal = queue_command(buffer);
879 }
880 return retVal;
881 }
882
883 bool VirtualTerminalClient::send_draw_text(std::uint16_t objectID, bool transparent, std::uint8_t textLength, const char *value)
884 {
885 bool retVal = false;
886
887 if ((nullptr != value) &&
888 (0 != textLength))
889 {
890 std::uint16_t messageLength = (6 + textLength);
891 std::vector<std::uint8_t> buffer;
892 buffer.resize(messageLength);
893 buffer[0] = static_cast<std::uint8_t>(Function::GraphicsContextCommand);
894 buffer[1] = static_cast<std::uint8_t>(objectID & 0xFF);
895 buffer[2] = static_cast<std::uint8_t>(objectID >> 8);
896 buffer[3] = static_cast<std::uint8_t>(GraphicsContextSubCommandID::DrawText);
897 buffer[4] = static_cast<std::uint8_t>(transparent);
898 buffer[5] = textLength;
899 memcpy(&buffer[6], value, textLength);
900
901 while (buffer.size() < CAN_DATA_LENGTH)
902 {
903 buffer.push_back(0xFF); // Pad short text to minimum message length
904 }
905 retVal = queue_command(buffer);
906 }
907 return retVal;
908 }
909
910 bool VirtualTerminalClient::send_pan_viewport(std::uint16_t objectID, std::int16_t xAttribute, std::int16_t yAttribute)
911 {
912 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::GraphicsContextCommand),
913 static_cast<std::uint8_t>(objectID & 0xFF),
914 static_cast<std::uint8_t>(objectID >> 8),
915 static_cast<std::uint8_t>(GraphicsContextSubCommandID::PanViewport),
916 static_cast<std::uint8_t>(xAttribute & 0xFF),
917 static_cast<std::uint8_t>(xAttribute >> 8),
918 static_cast<std::uint8_t>(yAttribute & 0xFF),
919 static_cast<std::uint8_t>(yAttribute >> 8) };
920 return queue_command(buffer);
921 }
922
923 bool VirtualTerminalClient::send_zoom_viewport(std::uint16_t objectID, float zoom)
924 {
925 static_assert(sizeof(float) == 4, "Float must be 4 bytes");
926 std::array<std::uint8_t, sizeof(float)> floatBytes = { 0 };
927 memcpy(floatBytes.data(), &zoom, sizeof(float));
928
929 if (is_big_endian())
930 {
931 std::reverse(floatBytes.begin(), floatBytes.end());
932 }
933
934 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::GraphicsContextCommand),
935 static_cast<std::uint8_t>(objectID & 0xFF),
936 static_cast<std::uint8_t>(objectID >> 8),
937 static_cast<std::uint8_t>(GraphicsContextSubCommandID::ZoomViewport),
938 floatBytes[0],
939 floatBytes[1],
940 floatBytes[2],
941 floatBytes[3] };
942 return queue_command(buffer);
943 }
944
945 bool VirtualTerminalClient::send_pan_and_zoom_viewport(std::uint16_t objectID, std::int16_t xAttribute, std::int16_t yAttribute, float zoom)
946 {
947 static_assert(sizeof(float) == 4, "Float must be 4 bytes");
948 std::array<std::uint8_t, sizeof(float)> floatBytes = { 0 };
949 memcpy(floatBytes.data(), &zoom, sizeof(float));
950
951 if (is_big_endian())
952 {
953 std::reverse(floatBytes.begin(), floatBytes.end());
954 }
955
956 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::GraphicsContextCommand),
957 static_cast<std::uint8_t>(objectID & 0xFF),
958 static_cast<std::uint8_t>(objectID >> 8),
959 static_cast<std::uint8_t>(GraphicsContextSubCommandID::PanAndZoomViewport),
960 static_cast<std::uint8_t>(xAttribute & 0xFF),
961 static_cast<std::uint8_t>(xAttribute >> 8),
962 static_cast<std::uint8_t>(yAttribute & 0xFF),
963 static_cast<std::uint8_t>(yAttribute >> 8),
964 floatBytes[0],
965 floatBytes[1],
966 floatBytes[2],
967 floatBytes[3] };
968 return queue_command(buffer);
969 }
970
971 bool VirtualTerminalClient::send_change_viewport_size(std::uint16_t objectID, std::uint16_t width, std::uint16_t height)
972 {
973 constexpr std::uint16_t MAX_WIDTH_HEIGHT = 32767;
974 bool retVal = false;
975
976 if ((width <= MAX_WIDTH_HEIGHT) &&
977 (height <= MAX_WIDTH_HEIGHT))
978 {
979 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::GraphicsContextCommand),
980 static_cast<std::uint8_t>(objectID & 0xFF),
981 static_cast<std::uint8_t>(objectID >> 8),
982 static_cast<std::uint8_t>(GraphicsContextSubCommandID::ChangeViewportSize),
983 static_cast<std::uint8_t>(width & 0xFF),
984 static_cast<std::uint8_t>(width >> 8),
985 static_cast<std::uint8_t>(height & 0xFF),
986 static_cast<std::uint8_t>(height >> 8) };
987 retVal = queue_command(buffer);
988 }
989 return retVal;
990 }
991
992 bool VirtualTerminalClient::send_draw_vt_object(std::uint16_t graphicsContextObjectID, std::uint16_t objectID)
993 {
994 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::GraphicsContextCommand),
995 static_cast<std::uint8_t>(graphicsContextObjectID & 0xFF),
996 static_cast<std::uint8_t>(graphicsContextObjectID >> 8),
997 static_cast<std::uint8_t>(GraphicsContextSubCommandID::DrawVTObject),
998 static_cast<std::uint8_t>(objectID & 0xFF),
999 static_cast<std::uint8_t>(objectID >> 8),
1000 0xFF,
1001 0xFF };
1002 return queue_command(buffer);
1003 }
1004
1005 bool VirtualTerminalClient::send_copy_canvas_to_picture_graphic(std::uint16_t graphicsContextObjectID, std::uint16_t objectID)
1006 {
1007 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::GraphicsContextCommand),
1008 static_cast<std::uint8_t>(graphicsContextObjectID & 0xFF),
1009 static_cast<std::uint8_t>(graphicsContextObjectID >> 8),
1011 static_cast<std::uint8_t>(objectID & 0xFF),
1012 static_cast<std::uint8_t>(objectID >> 8),
1013 0xFF,
1014 0xFF };
1015 return queue_command(buffer);
1016 }
1017
1018 bool VirtualTerminalClient::send_copy_viewport_to_picture_graphic(std::uint16_t graphicsContextObjectID, std::uint16_t objectID)
1019 {
1020 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::GraphicsContextCommand),
1021 static_cast<std::uint8_t>(graphicsContextObjectID & 0xFF),
1022 static_cast<std::uint8_t>(graphicsContextObjectID >> 8),
1024 static_cast<std::uint8_t>(objectID & 0xFF),
1025 static_cast<std::uint8_t>(objectID >> 8),
1026 0xFF,
1027 0xFF };
1028 return queue_command(buffer);
1029 }
1030
1031 bool VirtualTerminalClient::send_get_attribute_value(std::uint16_t objectID, std::uint8_t attributeID)
1032 {
1033 const std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::GetAttributeValueMessage),
1034 static_cast<std::uint8_t>(objectID & 0xFF),
1035 static_cast<std::uint8_t>(objectID >> 8),
1036 attributeID,
1037 0xFF,
1038 0xFF,
1039 0xFF,
1040 0xFF };
1041 return queue_command(buffer, true);
1042 }
1043
1045 {
1046 return softKeyXAxisPixels;
1047 }
1048
1050 {
1051 return softKeyYAxisPixels;
1052 }
1053
1058
1063
1065 {
1066 bool retVal = false;
1067
1068 switch (value)
1069 {
1070 case FontSize::Size6x8:
1071 case FontSize::Size8x8:
1072 case FontSize::Size8x12:
1078 {
1079 retVal = (0 != (smallFontSizesBitfield & (1 << static_cast<std::uint8_t>(value))));
1080 }
1081 break;
1082
1090 {
1091 retVal = (0 != (largeFontSizesBitfield & (1 << (static_cast<std::uint8_t>(value) - static_cast<std::uint8_t>(FontSize::Size32x48)))));
1092 }
1093 break;
1094 }
1095 return retVal;
1096 }
1097
1099 {
1100 return (0 != (static_cast<std::uint8_t>(value) & fontStylesBitfield));
1101 }
1102
1107
1112
1117
1119 {
1120 return (0 != (hardwareFeaturesBitfield & 0x04));
1121 }
1122
1124 {
1125 return (0 != (hardwareFeaturesBitfield & 0x08));
1126 }
1127
1132
1137
1139 {
1140 return (0 != (hardwareFeaturesBitfield & 0x40));
1141 }
1142
1147
1149 {
1150 return xPixels;
1151 }
1152
1154 {
1155 return yPixels;
1156 }
1157
1159 {
1160 VTVersion retVal;
1161
1162 switch (connectedVTVersion)
1163 {
1164 case 0x03:
1165 {
1166 retVal = VTVersion::Version3;
1167 }
1168 break;
1169
1170 case 0x04:
1171 {
1172 retVal = VTVersion::Version4;
1173 }
1174 break;
1175
1176 case 0x05:
1177 {
1178 retVal = VTVersion::Version5;
1179 }
1180 break;
1181
1182 case 0x06:
1183 {
1184 retVal = VTVersion::Version6;
1185 }
1186 break;
1187
1188 case 0xFF:
1189 {
1191 }
1192 break;
1193
1194 default:
1195 {
1197 }
1198 break;
1199 }
1200 return retVal;
1201 }
1202
1204 {
1205 bool retVal = false;
1206
1208 {
1209 retVal = get_connected_vt_version() >= minimumVersion;
1210 }
1211
1212 return retVal;
1213 }
1214
1219
1224
1225 void VirtualTerminalClient::set_object_pool(std::uint8_t poolIndex, const std::uint8_t *pool, std::uint32_t size, std::string version)
1226 {
1227 if ((nullptr != pool) &&
1228 (0 != size))
1229 {
1230 ObjectPoolDataStruct tempData;
1231
1232 tempData.objectPoolDataPointer = pool;
1233 tempData.objectPoolVectorPointer = nullptr;
1234 tempData.dataCallback = nullptr;
1235 tempData.objectPoolSize = size;
1238 tempData.useDataCallback = false;
1239 tempData.uploaded = false;
1240 tempData.versionLabel = version;
1241
1242 if (poolIndex < objectPools.size())
1243 {
1244 objectPools[poolIndex] = tempData;
1245 }
1246 else
1247 {
1248 objectPools.resize(poolIndex + 1);
1249 objectPools[poolIndex] = tempData;
1250 }
1251 }
1252 }
1253
1254 void VirtualTerminalClient::set_object_pool(std::uint8_t poolIndex, const std::vector<std::uint8_t> *pool, std::string version)
1255 {
1256 if ((nullptr != pool) &&
1257 (0 != pool->size()))
1258 {
1259 ObjectPoolDataStruct tempData;
1260
1261 tempData.objectPoolDataPointer = nullptr;
1262 tempData.objectPoolVectorPointer = pool;
1263 tempData.dataCallback = nullptr;
1264 tempData.objectPoolSize = pool->size();
1267 tempData.useDataCallback = false;
1268 tempData.uploaded = false;
1269 tempData.versionLabel = version;
1270
1271 if (poolIndex < objectPools.size())
1272 {
1273 objectPools[poolIndex] = tempData;
1274 }
1275 else
1276 {
1277 objectPools.resize(poolIndex + 1);
1278 objectPools[poolIndex] = tempData;
1279 }
1280 }
1281 }
1282
1284 std::uint32_t originalDataMaskDimensions_px,
1285 std::uint32_t originalSoftKyeDesignatorHeight_px)
1286 {
1287 // You have to call set_object_pool or register_object_pool_data_chunk_callback before calling this function
1288 assert(poolIndex < objectPools.size());
1289 objectPools[poolIndex].autoScaleDataMaskOriginalDimension = originalDataMaskDimensions_px;
1290 objectPools[poolIndex].autoScaleSoftKeyDesignatorOriginalHeight = originalSoftKyeDesignatorHeight_px;
1291 }
1292
1293 void VirtualTerminalClient::register_object_pool_data_chunk_callback(std::uint8_t poolIndex, std::uint32_t poolTotalSize, DataChunkCallback value, std::string version)
1294 {
1295 if ((nullptr != value) &&
1296 (0 != poolTotalSize))
1297 {
1298 ObjectPoolDataStruct tempData;
1299
1300 tempData.objectPoolDataPointer = nullptr;
1301 tempData.objectPoolVectorPointer = nullptr;
1302 tempData.dataCallback = value;
1303 tempData.objectPoolSize = poolTotalSize;
1304 tempData.useDataCallback = true;
1305 tempData.uploaded = false;
1308 tempData.versionLabel = version;
1309
1310 if (poolIndex < objectPools.size())
1311 {
1312 objectPools[poolIndex] = tempData;
1313 }
1314 else
1315 {
1316 objectPools.resize(poolIndex + 1);
1317 objectPools[poolIndex] = tempData;
1318 }
1319 }
1320 }
1321
1323 {
1324 StateMachineState previousStateMachineState = state; // Save state to see if it changes this update
1325
1326 if (nullptr != partnerControlFunction)
1327 {
1328 switch (state)
1329 {
1331 {
1334 unsupportedFunctions.clear();
1335
1336 if (partnerControlFunction->get_address_valid())
1337 {
1339 }
1340 }
1341 break;
1342
1344 {
1345 if (0 != lastVTStatusTimestamp_ms)
1346 {
1348 }
1349 }
1350 break;
1351
1353 {
1355 {
1357 }
1358 }
1359 break;
1360
1362 {
1363 // If we're in this state, we are ready to upload the
1364 // object pool but no pool has been set to this class
1365 // so the state machine cannot progress.
1366 if (SystemTiming::time_expired_ms(lastVTStatusTimestamp_ms, VT_STATUS_TIMEOUT_MS))
1367 {
1368 LOG_ERROR("[VT]: Ready to upload pool, but VT server has timed out. Disconnecting.");
1370 }
1371
1372 if (0 != objectPools.size())
1373 {
1376 lastWorkingSetMaintenanceTimestamp_ms = SystemTiming::get_timestamp_ms();
1379 }
1380 }
1381 break;
1382
1384 {
1385 std::uint32_t totalPoolSize = 0;
1386
1387 for (auto &pool : objectPools)
1388 {
1389 totalPoolSize += pool.objectPoolSize;
1390 }
1391
1392 if (send_get_memory(totalPoolSize))
1393 {
1395 }
1396 }
1397 break;
1398
1400 {
1401 if (SystemTiming::time_expired_ms(stateMachineTimestamp_ms, VT_STATUS_TIMEOUT_MS))
1402 {
1404 LOG_ERROR("[VT]: Get Memory Response Timeout");
1405 }
1406 }
1407 break;
1408
1410 {
1412 {
1414 }
1415 }
1416 break;
1417
1419 {
1420 if (SystemTiming::time_expired_ms(stateMachineTimestamp_ms, VT_STATUS_TIMEOUT_MS))
1421 {
1423 LOG_ERROR("[VT]: Get Number Softkeys Response Timeout");
1424 }
1425 }
1426 break;
1427
1429 {
1431 {
1433 }
1434 }
1435 break;
1436
1438 {
1439 if (SystemTiming::time_expired_ms(stateMachineTimestamp_ms, VT_STATUS_TIMEOUT_MS))
1440 {
1442 LOG_ERROR("[VT]: Get Text Font Data Response Timeout");
1443 }
1444 }
1445 break;
1446
1448 {
1449 if (send_get_hardware())
1450 {
1452 }
1453 }
1454 break;
1455
1457 {
1458 if (SystemTiming::time_expired_ms(stateMachineTimestamp_ms, VT_STATUS_TIMEOUT_MS))
1459 {
1461 LOG_ERROR("[VT]: Get Hardware Response Timeout");
1462 }
1463 }
1464 break;
1465
1467 {
1468 if (SystemTiming::time_expired_ms(stateMachineTimestamp_ms, VT_STATUS_TIMEOUT_MS))
1469 {
1471 LOG_ERROR("[VT]: Get Versions Timeout");
1472 }
1473 else if ((!objectPools.empty()) &&
1474 (!objectPools[0].versionLabel.empty()) &&
1476 {
1478 }
1479 }
1480 break;
1481
1483 {
1484 if (SystemTiming::time_expired_ms(stateMachineTimestamp_ms, VT_STATUS_TIMEOUT_MS))
1485 {
1487 LOG_ERROR("[VT]: Get Versions Response Timeout");
1488 }
1489 }
1490 break;
1491
1493 {
1494 if (SystemTiming::time_expired_ms(stateMachineTimestamp_ms, VT_STATUS_TIMEOUT_MS))
1495 {
1497 LOG_ERROR("[VT]: Send Load Version Timeout");
1498 }
1499 else
1500 {
1501 constexpr std::uint8_t VERSION_LABEL_LENGTH = 7;
1502 std::array<std::uint8_t, VERSION_LABEL_LENGTH> tempVersionBuffer;
1503
1504 // Unused bytes filled with spaces
1505 tempVersionBuffer[0] = ' ';
1506 tempVersionBuffer[1] = ' ';
1507 tempVersionBuffer[2] = ' ';
1508 tempVersionBuffer[3] = ' ';
1509 tempVersionBuffer[4] = ' ';
1510 tempVersionBuffer[5] = ' ';
1511 tempVersionBuffer[6] = ' ';
1512
1513 for (std::size_t i = 0; ((i < VERSION_LABEL_LENGTH) && (i < objectPools[0].versionLabel.size())); i++)
1514 {
1515 tempVersionBuffer[i] = objectPools[0].versionLabel[i];
1516 }
1517
1518 if (send_load_version(tempVersionBuffer))
1519 {
1521 }
1522 }
1523 }
1524 break;
1525
1527 {
1528 if (SystemTiming::time_expired_ms(stateMachineTimestamp_ms, VT_STATUS_TIMEOUT_MS))
1529 {
1531 LOG_ERROR("[VT]: Load Version Response Timeout");
1532 }
1533 }
1534 break;
1535
1537 {
1538 if (SystemTiming::time_expired_ms(stateMachineTimestamp_ms, VT_STATUS_TIMEOUT_MS))
1539 {
1541 LOG_ERROR("[VT]: Send Store Version Timeout");
1542 }
1543 else
1544 {
1545 constexpr std::uint8_t VERSION_LABEL_LENGTH = 7;
1546 std::array<std::uint8_t, VERSION_LABEL_LENGTH> tempVersionBuffer;
1547
1548 // Unused bytes filled with spaces
1549 tempVersionBuffer[0] = ' ';
1550 tempVersionBuffer[1] = ' ';
1551 tempVersionBuffer[2] = ' ';
1552 tempVersionBuffer[3] = ' ';
1553 tempVersionBuffer[4] = ' ';
1554 tempVersionBuffer[5] = ' ';
1555 tempVersionBuffer[6] = ' ';
1556
1557 for (std::size_t i = 0; ((i < VERSION_LABEL_LENGTH) && (i < objectPools[0].versionLabel.size())); i++)
1558 {
1559 tempVersionBuffer[i] = objectPools[0].versionLabel[i];
1560 }
1561
1562 if (send_store_version(tempVersionBuffer))
1563 {
1565 }
1566 }
1567 }
1568 break;
1569
1571 {
1572 if (SystemTiming::time_expired_ms(stateMachineTimestamp_ms, VT_STATUS_TIMEOUT_MS))
1573 {
1575 LOG_ERROR("[VT]: Store Version Response Timeout");
1576 }
1577 }
1578 break;
1579
1581 {
1582 bool allPoolsProcessed = true;
1583
1584 if (firstTimeInState)
1585 {
1587 {
1588 // Scale object pools before upload.
1589 if (!scale_object_pools())
1590 {
1592 }
1593 }
1594 }
1595
1596 for (std::uint32_t i = 0; i < objectPools.size(); i++)
1597 {
1598 if (((nullptr != objectPools[i].objectPoolDataPointer) ||
1599 (nullptr != objectPools[i].dataCallback)) &&
1600 (objectPools[i].objectPoolSize > 0))
1601 {
1602 if (!objectPools[i].uploaded)
1603 {
1604 allPoolsProcessed = false;
1605 }
1606
1608 {
1609 if (!objectPools[i].uploaded)
1610 {
1611 bool transmitSuccessful = CANNetworkManager::CANNetwork.send_can_message(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ECUtoVirtualTerminal),
1612 nullptr,
1613 objectPools[i].objectPoolSize + 1, // Account for Mux byte
1618 this,
1620
1621 if (transmitSuccessful)
1622 {
1624 }
1625 }
1626 else
1627 {
1628 // Pool already uploaded, move on to the next one
1629 }
1630 }
1632 {
1633 if (false == objectPools[i].uploaded)
1634 {
1635 objectPools[i].uploaded = true;
1636 LOG_DEBUG("[VT]: Object pool %u uploaded.", i + 1);
1638 }
1639 }
1641 {
1643 LOG_ERROR("[VT]: An object pool failed to upload. Resetting connection to VT.");
1645 }
1646 else
1647 {
1648 // Transfer is in progress. Nothing to do now.
1649 allPoolsProcessed = false;
1650 break;
1651 }
1652 }
1653 else
1654 {
1655 LOG_WARNING("[VT]: An object pool was supplied with an invalid size or pointer. Ignoring it.");
1656 objectPools[i].uploaded = true;
1657 }
1658 }
1659
1660 if (allPoolsProcessed)
1661 {
1663 }
1664 }
1665 break;
1666
1668 {
1670 {
1672 }
1673 }
1674 break;
1675
1677 {
1678 if (SystemTiming::time_expired_ms(stateMachineTimestamp_ms, VT_STATUS_TIMEOUT_MS))
1679 {
1681 LOG_ERROR("[VT]: Get End of Object Pool Response Timeout");
1682 }
1683 }
1684 break;
1685
1687 {
1688 // Check for timeouts
1689 if (SystemTiming::time_expired_ms(lastVTStatusTimestamp_ms, VT_STATUS_TIMEOUT_MS))
1690 {
1692 LOG_ERROR("[VT]: Status Timeout");
1693 }
1695 }
1696 break;
1697
1699 {
1700 constexpr std::uint32_t VT_STATE_MACHINE_RETRY_TIMEOUT_MS = 5000;
1703
1704 // Retry connecting after a while
1705 if (SystemTiming::time_expired_ms(stateMachineTimestamp_ms, VT_STATE_MACHINE_RETRY_TIMEOUT_MS))
1706 {
1707 LOG_INFO("[VT]: Resetting Failed VT Connection");
1709 }
1710 }
1711 break;
1712
1713 default:
1714 {
1715 }
1716 break;
1717 }
1718 }
1719 else
1720 {
1722 }
1723
1726 {
1727 txFlags.set_flag(static_cast<std::uint32_t>(TransmitFlags::SendWorkingSetMaintenance));
1728 }
1730 (!ourAuxiliaryInputs.empty()) &&
1732 {
1734 txFlags.set_flag(static_cast<std::uint32_t>(TransmitFlags::SendAuxiliaryMaintenance));
1735 }
1736 txFlags.process_all_flags();
1738
1739 if (state == previousStateMachineState)
1740 {
1741 firstTimeInState = false;
1742 }
1743 }
1744
1745 bool VirtualTerminalClient::send_message_to_vt(const std::uint8_t *dataBuffer, std::uint32_t dataLength, CANIdentifier::CANPriority priority) const
1746 {
1747 return CANNetworkManager::CANNetwork.send_can_message(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ECUtoVirtualTerminal),
1748 dataBuffer,
1749 dataLength,
1752 priority);
1753 }
1754
1756 {
1757 constexpr std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = { static_cast<std::uint8_t>(Function::DeleteObjectPoolCommand),
1758 0xFF,
1759 0xFF,
1760 0xFF,
1761 0xFF,
1762 0xFF,
1763 0xFF,
1764 0xFF };
1765 return send_message_to_vt(buffer.data(), buffer.size());
1766 }
1767
1769 {
1770 static constexpr std::uint8_t SUPPORTED_VT_VERSION = 0x06;
1771
1772 std::uint8_t bitmask = (initializing ? 0x01 : 0x00);
1773
1774 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = { static_cast<std::uint8_t>(Function::WorkingSetMaintenanceMessage),
1775 bitmask,
1776 SUPPORTED_VT_VERSION,
1777 0xFF,
1778 0xFF,
1779 0xFF,
1780 0xFF,
1781 0xFF };
1782 return send_message_to_vt(buffer.data(), buffer.size());
1783 }
1784
1785 bool VirtualTerminalClient::send_get_memory(std::uint32_t requiredMemory) const
1786 {
1787 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = { static_cast<std::uint8_t>(Function::GetMemoryMessage),
1788 0xFF,
1789 static_cast<std::uint8_t>(requiredMemory & 0xFF),
1790 static_cast<std::uint8_t>((requiredMemory >> 8) & 0xFF),
1791 static_cast<std::uint8_t>((requiredMemory >> 16) & 0xFF),
1792 static_cast<std::uint8_t>((requiredMemory >> 24) & 0xFF),
1793 0xFF,
1794 0xFF };
1795 return send_message_to_vt(buffer.data(), buffer.size());
1796 }
1797
1799 {
1800 constexpr std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = { static_cast<std::uint8_t>(Function::GetNumberOfSoftKeysMessage),
1801 0xFF,
1802 0xFF,
1803 0xFF,
1804 0xFF,
1805 0xFF,
1806 0xFF,
1807 0xFF };
1808 return send_message_to_vt(buffer.data(), buffer.size());
1809 }
1810
1812 {
1813 constexpr std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = { static_cast<std::uint8_t>(Function::GetTextFontDataMessage),
1814 0xFF,
1815 0xFF,
1816 0xFF,
1817 0xFF,
1818 0xFF,
1819 0xFF,
1820 0xFF };
1821 return send_message_to_vt(buffer.data(), buffer.size());
1822 }
1823
1825 {
1826 constexpr std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = { static_cast<std::uint8_t>(Function::GetHardwareMessage),
1827 0xFF,
1828 0xFF,
1829 0xFF,
1830 0xFF,
1831 0xFF,
1832 0xFF,
1833 0xFF };
1834 return send_message_to_vt(buffer.data(), buffer.size());
1835 }
1836
1838 {
1839 constexpr std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = { static_cast<std::uint8_t>(Function::GetSupportedWidecharsMessage),
1840 0xFF,
1841 0xFF,
1842 0xFF,
1843 0xFF,
1844 0xFF,
1845 0xFF,
1846 0xFF };
1847 return send_message_to_vt(buffer.data(), buffer.size());
1848 }
1849
1851 {
1852 constexpr std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = { static_cast<std::uint8_t>(Function::GetWindowMaskDataMessage),
1853 0xFF,
1854 0xFF,
1855 0xFF,
1856 0xFF,
1857 0xFF,
1858 0xFF,
1859 0xFF };
1860 return send_message_to_vt(buffer.data(), buffer.size());
1861 }
1862
1864 {
1865 constexpr std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = { static_cast<std::uint8_t>(Function::GetSupportedObjectsMessage),
1866 0xFF,
1867 0xFF,
1868 0xFF,
1869 0xFF,
1870 0xFF,
1871 0xFF,
1872 0xFF };
1873 return send_message_to_vt(buffer.data(), buffer.size());
1874 }
1875
1877 {
1878 constexpr std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = { static_cast<std::uint8_t>(Function::GetVersionsMessage),
1879 0xFF,
1880 0xFF,
1881 0xFF,
1882 0xFF,
1883 0xFF,
1884 0xFF,
1885 0xFF };
1886 return send_message_to_vt(buffer.data(), buffer.size());
1887 }
1888
1889 bool VirtualTerminalClient::send_store_version(std::array<std::uint8_t, 7> versionLabel) const
1890 {
1891 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = { static_cast<std::uint8_t>(Function::StoreVersionCommand),
1892 versionLabel[0],
1893 versionLabel[1],
1894 versionLabel[2],
1895 versionLabel[3],
1896 versionLabel[4],
1897 versionLabel[5],
1898 versionLabel[6] };
1899 return send_message_to_vt(buffer.data(), buffer.size());
1900 }
1901
1902 bool VirtualTerminalClient::send_load_version(std::array<std::uint8_t, 7> versionLabel) const
1903 {
1904 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = { static_cast<std::uint8_t>(Function::LoadVersionCommand),
1905 versionLabel[0],
1906 versionLabel[1],
1907 versionLabel[2],
1908 versionLabel[3],
1909 versionLabel[4],
1910 versionLabel[5],
1911 versionLabel[6] };
1912 return send_message_to_vt(buffer.data(), buffer.size());
1913 }
1914
1915 bool VirtualTerminalClient::send_delete_version(std::array<std::uint8_t, 7> versionLabel) const
1916 {
1917 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = { static_cast<std::uint8_t>(Function::DeleteVersionCommand),
1918 versionLabel[0],
1919 versionLabel[1],
1920 versionLabel[2],
1921 versionLabel[3],
1922 versionLabel[4],
1923 versionLabel[5],
1924 versionLabel[6] };
1925 return send_message_to_vt(buffer.data(), buffer.size());
1926 }
1927
1929 {
1930 constexpr std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = { static_cast<std::uint8_t>(Function::ExtendedDeleteVersionCommand),
1931 0xFF,
1932 0xFF,
1933 0xFF,
1934 0xFF,
1935 0xFF,
1936 0xFF,
1937 0xFF };
1938 return send_message_to_vt(buffer.data(), buffer.size());
1939 }
1940
1941 bool VirtualTerminalClient::send_extended_store_version(std::array<std::uint8_t, 32> versionLabel) const
1942 {
1943 std::array<std::uint8_t, 33> buffer;
1944 buffer[0] = static_cast<std::uint8_t>(Function::ExtendedStoreVersionCommand);
1945 memcpy(&buffer[1], versionLabel.data(), 32);
1946 return send_message_to_vt(buffer.data(), buffer.size());
1947 }
1948
1949 bool VirtualTerminalClient::send_extended_load_version(std::array<std::uint8_t, 32> versionLabel) const
1950 {
1951 std::array<std::uint8_t, 33> buffer;
1952 buffer[0] = static_cast<std::uint8_t>(Function::ExtendedLoadVersionCommand);
1953 memcpy(&buffer[1], versionLabel.data(), 32);
1954 return send_message_to_vt(buffer.data(), buffer.size());
1955 }
1956
1957 bool VirtualTerminalClient::send_extended_delete_version(std::array<std::uint8_t, 32> versionLabel) const
1958 {
1959 std::array<std::uint8_t, 33> buffer;
1960 buffer[0] = static_cast<std::uint8_t>(Function::ExtendedDeleteVersionCommand);
1961 memcpy(&buffer[1], versionLabel.data(), 32);
1962 return send_message_to_vt(buffer.data(), buffer.size());
1963 }
1964
1966 {
1967 constexpr std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = { static_cast<std::uint8_t>(Function::EndOfObjectPoolMessage),
1968 0xFF,
1969 0xFF,
1970 0xFF,
1971 0xFF,
1972 0xFF,
1973 0xFF,
1974 0xFF };
1975 return send_message_to_vt(buffer.data(), buffer.size());
1976 }
1977
1979 {
1980 constexpr std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = { 0x01,
1981 0xFF,
1982 0xFF,
1983 0xFF,
1984 0xFF,
1985 0xFF,
1986 0xFF,
1987 0xFF };
1988 return CANNetworkManager::CANNetwork.send_can_message(static_cast<std::uint32_t>(CANLibParameterGroupNumber::WorkingSetMaster),
1989 buffer.data(),
1992 nullptr,
1994 }
1995
1997 {
1999 std::vector<std::uint8_t> buffer = { static_cast<std::uint8_t>(Function::PreferredAssignmentCommand), 0 };
2000 if (buffer.size() < CAN_DATA_LENGTH)
2001 {
2002 buffer.resize(CAN_DATA_LENGTH);
2003 }
2004 return send_message_to_vt(buffer.data(), buffer.size());
2005 }
2006
2007 bool VirtualTerminalClient::send_auxiliary_function_assignment_response(std::uint16_t functionObjectID, bool hasError, bool isAlreadyAssigned) const
2008 {
2009 std::uint8_t errorCode = 0;
2010 if (hasError)
2011 {
2012 errorCode |= 0x01;
2013 }
2014 if ((isAlreadyAssigned) && (false == is_vt_version_supported(VTVersion::Version6)))
2015 {
2016 errorCode |= 0x02;
2017 }
2018 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = { static_cast<std::uint8_t>(Function::AuxiliaryAssignmentTypeTwoCommand),
2019 static_cast<std::uint8_t>(functionObjectID),
2020 static_cast<std::uint8_t>(functionObjectID >> 8),
2021 errorCode,
2022 0xFF,
2023 0xFF,
2024 0xFF,
2025 0xFF };
2026 return send_message_to_vt(buffer.data(), buffer.size());
2027 }
2028
2030 {
2031 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = { static_cast<std::uint8_t>(Function::AuxiliaryInputTypeTwoMaintenanceMessage),
2032 static_cast<std::uint8_t>(ourModelIdentificationCode),
2033 static_cast<std::uint8_t>(ourModelIdentificationCode >> 8),
2034 static_cast<std::uint8_t>(StateMachineState::Connected == state ? 0x01 : 0x00),
2035 0xFF,
2036 0xFF,
2037 0xFF,
2038 0xFF };
2039 return CANNetworkManager::CANNetwork.send_can_message(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ECUtoVirtualTerminal),
2040 buffer.data(),
2043 nullptr,
2045 }
2046
2047 bool VirtualTerminalClient::send_auxiliary_input_status_enable_response(std::uint16_t objectID, bool isEnabled, bool invalidObjectID) const
2048 {
2049 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = { static_cast<std::uint8_t>(Function::AuxiliaryInputStatusTypeTwoEnableCommand),
2050 static_cast<std::uint8_t>(objectID),
2051 static_cast<std::uint8_t>(objectID >> 8),
2052 static_cast<std::uint8_t>(isEnabled ? 0x01 : 0x00),
2053 static_cast<std::uint8_t>(invalidObjectID ? 0x01 : 0x00),
2054 0xFF,
2055 0xFF,
2056 0xFF };
2057 return send_message_to_vt(buffer.data(), buffer.size());
2058 }
2059
2061 {
2062 for (auto &auxiliaryInput : ourAuxiliaryInputs)
2063 {
2064 update_auxiliary_input_status(auxiliaryInput.first);
2065 }
2066 }
2067
2069 {
2070 bool retVal = false;
2073 if (SystemTiming::time_expired_ms(state.lastStatusUpdate, AUXILIARY_INPUT_STATUS_DELAY) ||
2074 (state.hasInteraction &&
2076 SystemTiming::time_expired_ms(state.lastStatusUpdate, AUXILIARY_INPUT_STATUS_DELAY_INTERACTION)))
2077 {
2078 state.lastStatusUpdate = SystemTiming::get_timestamp_ms();
2079
2080 std::uint8_t operatingState = 0;
2082 {
2083 operatingState |= 0x01;
2084 if (state.hasInteraction)
2085 {
2086 operatingState |= 0x02;
2087 }
2088 }
2089 if (state.controlLocked)
2090 {
2091 operatingState |= 0x04;
2092 if (state.hasInteraction)
2093 {
2094 operatingState |= 0x08;
2095 }
2096 }
2097 state.hasInteraction = false; // reset interaction flag
2098
2100 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = { static_cast<std::uint8_t>(Function::AuxiliaryInputTypeTwoStatusMessage),
2101 static_cast<std::uint8_t>(objectID),
2102 static_cast<std::uint8_t>(objectID >> 8),
2103 static_cast<std::uint8_t>(state.value1),
2104 static_cast<std::uint8_t>(state.value1 >> 8),
2105 static_cast<std::uint8_t>(state.value2),
2106 static_cast<std::uint8_t>(state.value2 >> 8),
2107 operatingState };
2109 {
2110 retVal = send_message_to_vt(buffer.data(), buffer.size(), CANIdentifier::CANPriority::Priority3);
2111 }
2112 else
2113 {
2114 retVal = CANNetworkManager::CANNetwork.send_can_message(static_cast<std::uint32_t>(CANLibParameterGroupNumber::VirtualTerminalToECU),
2115 buffer.data(),
2118 nullptr,
2120 }
2121 }
2122 return retVal;
2123 }
2124
2126 {
2127 stateMachineTimestamp_ms = SystemTiming::get_timestamp_ms();
2128
2129 if (value != state)
2130 {
2131 firstTimeInState = true;
2132 }
2133
2134 state = value;
2135
2137 {
2139 for (std::size_t i = 0; i < objectPools.size(); i++)
2140 {
2141 objectPools[i].uploaded = false;
2142 }
2143 }
2144 }
2145
2146 void VirtualTerminalClient::process_flags(std::uint32_t flag, void *parent)
2147 {
2148 if ((flag <= static_cast<std::uint32_t>(TransmitFlags::NumberFlags)) &&
2149 (nullptr != parent))
2150 {
2151 TransmitFlags flagToProcess = static_cast<TransmitFlags>(flag);
2152 VirtualTerminalClient *vtClient = static_cast<VirtualTerminalClient *>(parent);
2153 bool transmitSuccessful = false;
2154
2155 switch (flagToProcess)
2156 {
2158 {
2159 if (!vtClient->objectPools.empty())
2160 {
2161 transmitSuccessful = vtClient->send_working_set_maintenance(false);
2162
2163 if (transmitSuccessful)
2164 {
2165 vtClient->lastWorkingSetMaintenanceTimestamp_ms = SystemTiming::get_timestamp_ms();
2166 }
2167 }
2168 }
2169 break;
2170
2172 {
2173 transmitSuccessful = vtClient->send_auxiliary_input_maintenance();
2174
2175 if (transmitSuccessful)
2176 {
2177 vtClient->lastAuxiliaryMaintenanceTimestamp_ms = SystemTiming::get_timestamp_ms();
2178 }
2179 }
2180 break;
2181
2183 default:
2184 {
2185 }
2186 break;
2187 }
2188
2189 if (false == transmitSuccessful)
2190 {
2191 vtClient->txFlags.set_flag(flag);
2192 }
2193 }
2194 }
2195
2196 void VirtualTerminalClient::process_rx_message(const CANMessage &message, void *parentPointer)
2197 {
2198 VirtualTerminalClient *parentVT = static_cast<VirtualTerminalClient *>(parentPointer);
2199 if ((nullptr != parentPointer) &&
2200 (CAN_DATA_LENGTH <= message.get_data_length()) &&
2201 ((nullptr == message.get_destination_control_function()) ||
2202 (parentVT->myControlFunction == message.get_destination_control_function())))
2203 {
2204 switch (message.get_identifier().get_parameter_group_number())
2205 {
2206 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::Acknowledge):
2207 {
2208 if (AcknowledgementType::Negative == static_cast<AcknowledgementType>(message.get_uint8_at(0)))
2209 {
2210 std::uint32_t targetParameterGroupNumber = message.get_uint24_at(5);
2211 if (static_cast<std::uint32_t>(CANLibParameterGroupNumber::ECUtoVirtualTerminal) == targetParameterGroupNumber)
2212 {
2213 LOG_ERROR("[VT]: The VT Server is NACK-ing our VT messages. Disconnecting.");
2215 }
2216 }
2217 }
2218 break;
2219
2220 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::VirtualTerminalToECU):
2221 {
2222 switch (message.get_uint8_at(0))
2223 {
2224 case static_cast<std::uint8_t>(Function::SoftKeyActivationMessage):
2225 {
2226 std::uint8_t keyCode = message.get_uint8_at(1);
2227 if (keyCode <= static_cast<std::uint8_t>(KeyActivationCode::ButtonPressAborted))
2228 {
2229 std::uint16_t objectID = message.get_uint16_at(2);
2230 std::uint16_t parentObjectID = message.get_uint16_at(4);
2231 std::uint8_t keyNumber = message.get_uint8_at(6);
2232 std::uint8_t transactionNumber = 0xF;
2234 {
2235 transactionNumber = message.get_uint8_at(7) >> 4;
2236 }
2237
2238 parentVT->softKeyEventDispatcher.invoke({ parentVT, objectID, parentObjectID, keyNumber, static_cast<KeyActivationCode>(keyCode) });
2239
2240 // Send response
2241 std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
2242 static_cast<std::uint8_t>(Function::SoftKeyActivationMessage),
2243 keyCode,
2244 static_cast<std::uint8_t>(objectID),
2245 static_cast<std::uint8_t>(objectID >> 8),
2246 static_cast<std::uint8_t>(parentObjectID),
2247 static_cast<std::uint8_t>(parentObjectID >> 8),
2248 keyNumber,
2249 0xFF,
2250 };
2252 {
2253 buffer[7] = static_cast<std::uint8_t>(transactionNumber << 4 | 0x0F);
2254 }
2255 parentVT->send_message_to_vt(buffer.data(), buffer.size());
2256 }
2257 }
2258 break;
2259
2260 case static_cast<std::uint8_t>(Function::ButtonActivationMessage):
2261 {
2262 std::uint8_t keyCode = message.get_uint8_at(1);
2263 if (keyCode <= static_cast<std::uint8_t>(KeyActivationCode::ButtonPressAborted))
2264 {
2265 std::uint16_t objectID = message.get_uint16_at(2);
2266 std::uint16_t parentObjectID = message.get_uint16_at(4);
2267 std::uint8_t keyNumber = message.get_uint8_at(6);
2268 std::uint8_t transactionNumber = 0xF;
2270 {
2271 transactionNumber = message.get_uint8_at(7) >> 4;
2272 }
2273
2274 parentVT->buttonEventDispatcher.invoke({ parentVT, objectID, parentObjectID, keyNumber, static_cast<KeyActivationCode>(keyCode) });
2275
2276 // Send response
2277 std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
2278 static_cast<std::uint8_t>(Function::ButtonActivationMessage),
2279 keyCode,
2280 static_cast<std::uint8_t>(objectID),
2281 static_cast<std::uint8_t>(objectID >> 8),
2282 static_cast<std::uint8_t>(parentObjectID),
2283 static_cast<std::uint8_t>(parentObjectID >> 8),
2284 keyNumber,
2285 0xFF,
2286 };
2288 {
2289 buffer[7] = static_cast<std::uint8_t>(transactionNumber << 4 | 0x0F);
2290 }
2291 parentVT->send_message_to_vt(buffer.data(), buffer.size());
2292 }
2293 }
2294 break;
2295
2296 case static_cast<std::uint8_t>(Function::PointingEventMessage):
2297 {
2298 std::uint16_t xPosition = message.get_uint16_at(1);
2299 std::uint16_t yPosition = message.get_uint16_at(3);
2300
2301 std::uint8_t touchState = static_cast<std::uint8_t>(KeyActivationCode::ButtonPressedOrLatched);
2302 std::uint16_t parentMaskObjectID = NULL_OBJECT_ID;
2303 std::uint8_t transactionNumber = 0xF;
2305 {
2306 // VT version is 6 or later
2307 touchState = message.get_uint8_at(5) & 0x0F;
2308 transactionNumber = message.get_uint8_at(5) >> 4;
2309 parentMaskObjectID = message.get_uint16_at(6);
2310 }
2311 else if (parentVT->is_vt_version_supported(VTVersion::Version4))
2312 {
2313 // VT version is either 4 or 5
2314 touchState = message.get_uint8_at(5);
2315 }
2316
2317 if (touchState <= static_cast<std::uint8_t>(KeyActivationCode::ButtonPressAborted))
2318 {
2319 parentVT->pointingEventDispatcher.invoke({ parentVT, xPosition, yPosition, parentMaskObjectID, static_cast<KeyActivationCode>(touchState) });
2320 }
2321
2322 // Send response
2323 std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
2324 static_cast<std::uint8_t>(Function::PointingEventMessage),
2325 static_cast<std::uint8_t>(xPosition),
2326 static_cast<std::uint8_t>(xPosition >> 8),
2327 static_cast<std::uint8_t>(yPosition),
2328 static_cast<std::uint8_t>(yPosition >> 8),
2329 0xFF,
2330 0xFF,
2331 0xFF,
2332 };
2334 {
2335 // VT version is 6 or later
2336 buffer[5] = static_cast<std::uint8_t>((transactionNumber << 4) | touchState);
2337 }
2339 {
2340 // VT version is either 4 or 5
2341 buffer[5] = touchState;
2342 }
2343 parentVT->send_message_to_vt(buffer.data(), buffer.size());
2344 }
2345 break;
2346
2347 case static_cast<std::uint8_t>(Function::VTSelectInputObjectMessage):
2348 {
2349 std::uint16_t objectID = message.get_uint16_at(1);
2350 bool objectSelected = (0x01 == message.get_uint8_at(3));
2351 bool objectOpenForInput = true;
2352
2354 {
2355 objectOpenForInput = message.get_bool_at(4, 0);
2356 }
2357
2358 std::uint8_t transactionNumber = 0xF;
2360 {
2361 transactionNumber = message.get_uint8_at(7) >> 4;
2362 }
2363
2364 parentVT->selectInputObjectEventDispatcher.invoke({ parentVT, objectID, objectSelected, objectOpenForInput });
2365
2366 // Send response
2367 std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
2368 static_cast<std::uint8_t>(Function::VTSelectInputObjectMessage),
2369 static_cast<std::uint8_t>(objectID),
2370 static_cast<std::uint8_t>(objectID >> 8),
2371 static_cast<std::uint8_t>(objectSelected ? 0x01 : 0x00),
2372 0xFF,
2373 0xFF,
2374 0xFF,
2375 0xFF,
2376 };
2377
2379 {
2380 buffer[4] = (objectOpenForInput ? 0x01 : 0x00);
2381 }
2383 {
2384 buffer[7] = static_cast<std::uint8_t>(transactionNumber << 4 | 0x0F);
2385 }
2386
2387 parentVT->send_message_to_vt(buffer.data(), buffer.size());
2388 }
2389 break;
2390
2391 case static_cast<std::uint8_t>(Function::VTESCMessage):
2392 {
2393 std::uint16_t objectID = message.get_uint16_at(1);
2394 std::uint8_t errorCode = message.get_uint8_at(3) & 0x1F;
2395 if ((errorCode == static_cast<std::uint8_t>(ESCMessageErrorCode::OtherError)) ||
2396 (errorCode <= static_cast<std::uint8_t>(ESCMessageErrorCode::NoInputFieldOpen)))
2397 {
2398 std::uint8_t transactionNumber = 0xF;
2400 {
2401 // VT version is 6 or later
2402 transactionNumber = message.get_uint8_at(7) >> 4;
2403 }
2404
2405 parentVT->escMessageEventDispatcher.invoke({ parentVT, objectID, static_cast<ESCMessageErrorCode>(errorCode) });
2406
2407 // Send response
2408 std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
2409 static_cast<std::uint8_t>(Function::VTESCMessage),
2410 static_cast<std::uint8_t>(objectID),
2411 static_cast<std::uint8_t>(objectID >> 8),
2412 0xFF,
2413 0xFF,
2414 0xFF,
2415 0xFF,
2416 static_cast<std::uint8_t>((transactionNumber << 4) | 0x0F),
2417 };
2418 parentVT->send_message_to_vt(buffer.data(), buffer.size());
2419 }
2420 }
2421 break;
2422
2423 case static_cast<std::uint8_t>(Function::VTChangeNumericValueMessage):
2424 {
2425 std::uint16_t objectID = message.get_uint16_at(1);
2426 std::uint32_t value = message.get_uint32_at(4);
2427 std::uint8_t transactionNumber = 0xF;
2429 {
2430 // VT version is 6 or later
2431 transactionNumber = message.get_uint8_at(7) >> 4;
2432 }
2433
2434 parentVT->changeNumericValueEventDispatcher.invoke({ parentVT, value, objectID });
2435
2436 // Send response
2437 std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
2438 static_cast<std::uint8_t>(Function::VTChangeNumericValueMessage),
2439 static_cast<std::uint8_t>(objectID),
2440 static_cast<std::uint8_t>(objectID >> 8),
2441 0xFF,
2442 static_cast<std::uint8_t>(value),
2443 static_cast<std::uint8_t>(value >> 8),
2444 static_cast<std::uint8_t>(value >> 16),
2445 static_cast<std::uint8_t>(value >> 24),
2446 };
2448 {
2449 buffer[3] = static_cast<std::uint8_t>(transactionNumber << 4 | 0x0F);
2450 }
2451 parentVT->send_message_to_vt(buffer.data(), buffer.size());
2452 }
2453 break;
2454
2455 case static_cast<std::uint8_t>(Function::VTChangeActiveMaskMessage):
2456 {
2457 std::uint16_t maskObjectID = message.get_uint16_at(1);
2458
2459 bool missingObjects = message.get_bool_at(3, 2);
2460 bool maskOrChildHasErrors = message.get_bool_at(3, 3);
2461 bool anyOtherError = message.get_bool_at(3, 4);
2462 bool poolDeleted = message.get_bool_at(3, 5);
2463
2464 std::uint16_t errorObjectID = message.get_uint16_at(4);
2465 std::uint16_t parentObjectID = message.get_uint16_at(6);
2466
2467 parentVT->changeActiveMaskEventDispatcher.invoke({ parentVT,
2468 maskObjectID,
2469 errorObjectID,
2470 parentObjectID,
2471 missingObjects,
2472 maskOrChildHasErrors,
2473 anyOtherError,
2474 poolDeleted });
2475
2476 // Send response
2477 std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
2478 static_cast<std::uint8_t>(Function::VTChangeActiveMaskMessage),
2479 static_cast<std::uint8_t>(maskObjectID),
2480 static_cast<std::uint8_t>(maskObjectID >> 8),
2481 0xFF,
2482 0xFF,
2483 0xFF,
2484 0xFF,
2485 0xFF
2486 };
2487 parentVT->send_message_to_vt(buffer.data(), buffer.size());
2488 }
2489 break;
2490
2491 case static_cast<std::uint8_t>(Function::VTChangeSoftKeyMaskMessage):
2492 {
2493 std::uint16_t dataOrAlarmMaskID = message.get_uint16_at(1);
2494 std::uint16_t softKeyMaskID = message.get_uint16_at(3);
2495
2496 bool missingObjects = message.get_bool_at(5, 2);
2497 bool maskOrChildHasErrors = message.get_bool_at(5, 3);
2498 bool anyOtherError = message.get_bool_at(5, 4);
2499 bool poolDeleted = message.get_bool_at(5, 5);
2500
2501 parentVT->changeSoftKeyMaskEventDispatcher.invoke({ parentVT,
2502 dataOrAlarmMaskID,
2503 softKeyMaskID,
2504 missingObjects,
2505 maskOrChildHasErrors,
2506 anyOtherError,
2507 poolDeleted });
2508
2509 // Send response
2510 std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
2511 static_cast<std::uint8_t>(Function::VTChangeSoftKeyMaskMessage),
2512 static_cast<std::uint8_t>(dataOrAlarmMaskID),
2513 static_cast<std::uint8_t>(dataOrAlarmMaskID >> 8),
2514 static_cast<std::uint8_t>(softKeyMaskID),
2515 static_cast<std::uint8_t>(softKeyMaskID >> 8),
2516 0xFF,
2517 0xFF,
2518 0xFF
2519 };
2520 parentVT->send_message_to_vt(buffer.data(), buffer.size());
2521 }
2522 break;
2523
2524 case static_cast<std::uint8_t>(Function::VTChangeStringValueMessage):
2525 {
2526 std::uint16_t objectID = message.get_uint16_at(1);
2527 std::uint8_t stringLength = message.get_uint8_at(3);
2528 std::string value = std::string(message.get_data().begin() + 4, message.get_data().begin() + 4 + stringLength);
2529
2530 parentVT->changeStringValueEventDispatcher.invoke({ value, parentVT, objectID });
2531
2532 // Send response
2533 std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
2534 static_cast<std::uint8_t>(Function::VTChangeStringValueMessage),
2535 0xFF,
2536 0xFF,
2537 static_cast<std::uint8_t>(objectID),
2538 static_cast<std::uint8_t>(objectID >> 8),
2539 0xFF,
2540 0xFF,
2541 0xFF
2542 };
2543 parentVT->send_message_to_vt(buffer.data(), buffer.size());
2544 }
2545 break;
2546
2547 case static_cast<std::uint8_t>(Function::VTOnUserLayoutHideShowMessage):
2548 {
2549 std::uint16_t objectID = message.get_uint16_at(1);
2550 bool hidden = !message.get_bool_at(3, 0);
2551
2552 parentVT->userLayoutHideShowEventDispatcher.invoke({ parentVT, objectID, hidden });
2553
2554 // There could be two layout messages in one packet
2555 objectID = message.get_uint16_at(4);
2556 if (objectID != NULL_OBJECT_ID)
2557 {
2558 hidden = !message.get_bool_at(6, 0);
2559 parentVT->userLayoutHideShowEventDispatcher.invoke({ parentVT, objectID, hidden });
2560 }
2561
2562 // Send response
2563 std::array<std::uint8_t, CAN_DATA_LENGTH> buffer;
2564 std::copy_n(message.get_data().begin(), CAN_DATA_LENGTH, buffer.begin());
2565 // Make sure we comply with standard specifications
2567 {
2568 // VT version is 6 or later
2569 buffer[7] |= 0x0F;
2570 }
2571 else
2572 {
2573 // VT version is 5 or prior
2574 buffer[7] = 0xFF;
2575 }
2576 parentVT->send_message_to_vt(buffer.data(), buffer.size());
2577 }
2578 break;
2579
2580 case static_cast<std::uint8_t>(Function::VTControlAudioSignalTerminationMessage):
2581 {
2582 bool terminated = message.get_bool_at(1, 0);
2583
2584 parentVT->audioSignalTerminationEventDispatcher.invoke({ parentVT, terminated });
2585
2586 std::uint8_t transactionNumber = 0xF;
2588 {
2589 // VT version is 6 or later, send response (VT version 5 and prior does not have a response)
2590 transactionNumber = message.get_uint8_at(2) >> 4;
2591 std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
2592 static_cast<std::uint8_t>(Function::VTControlAudioSignalTerminationMessage),
2593 static_cast<std::uint8_t>(terminated ? 0x01 : 0x00),
2594 static_cast<std::uint8_t>((transactionNumber << 4) | 0x0F),
2595 0xFF,
2596 0xFF,
2597 0xFF,
2598 0xFF,
2599 0xFF,
2600 };
2601 parentVT->send_message_to_vt(buffer.data(), buffer.size());
2602 }
2603 }
2604 break;
2605
2606 case static_cast<std::uint8_t>(Function::PreferredAssignmentCommand):
2607 {
2608 if (message.get_bool_at(1, 0))
2609 {
2610 LOG_ERROR("[AUX-N]: Preferred Assignment Error - Auxiliary Input Unit(s) (NAME or Model Identification Code) not valid");
2611 }
2612 if (message.get_bool_at(1, 1))
2613 {
2614 LOG_ERROR("[AUX-N]: Preferred Assignment Error - Function Object ID(S) not valid");
2615 }
2616 if (message.get_bool_at(1, 2))
2617 {
2618 LOG_ERROR("[AUX-N]: Preferred Assignment Error - Input Object ID(s) not valid");
2619 }
2620 if (message.get_bool_at(1, 3))
2621 {
2622 LOG_ERROR("[AUX-N]: Preferred Assignment Error - Duplicate Object ID of Auxiliary Function");
2623 }
2624 if (message.get_bool_at(1, 4))
2625 {
2626 LOG_ERROR("[AUX-N]: Preferred Assignment Error - Other");
2627 }
2628
2629 if (0 != message.get_uint8_at(1))
2630 {
2631 std::uint16_t faultyObjectID = message.get_uint16_at(2);
2632 LOG_ERROR("[AUX-N]: Auxiliary Function Object ID of faulty assignment: " + isobus::to_string(faultyObjectID));
2633 }
2634 else
2635 {
2636 LOG_DEBUG("[AUX-N]: Preferred Assignment OK");
2638 }
2639 }
2640 break;
2641
2642 case static_cast<std::uint8_t>(Function::AuxiliaryAssignmentTypeTwoCommand):
2643 {
2644 if (14 == message.get_data_length())
2645 {
2646 std::uint64_t isoName = message.get_uint64_at(1);
2647 bool storeAsPreferred = message.get_bool_at(9, 7);
2648 std::uint8_t functionType = (message.get_uint8_at(9) & 0x1F);
2649 std::uint16_t inputObjectID = message.get_uint16_at(10);
2650 std::uint16_t functionObjectID = message.get_uint16_at(12);
2651
2652 bool hasError = false;
2653 bool isAlreadyAssigned = false;
2654 if (DEFAULT_NAME == isoName && 0x1F == functionType)
2655 {
2656 if (NULL_OBJECT_ID == functionObjectID)
2657 {
2659 {
2660 aux.functions.clear();
2661 }
2662 LOG_INFO("[AUX-N] Unassigned all functions");
2663 }
2664 else if (NULL_OBJECT_ID == inputObjectID)
2665 {
2667 {
2668 for (auto iter = aux.functions.begin(); iter != aux.functions.end();)
2669 {
2670 if (iter->functionObjectID == functionObjectID)
2671 {
2672 aux.functions.erase(iter);
2673 if (storeAsPreferred)
2674 {
2676 }
2677 LOG_INFO("[AUX-N] Unassigned function " + isobus::to_string(static_cast<int>(functionObjectID)) + " from input " + isobus::to_string(static_cast<int>(inputObjectID)));
2678 }
2679 else
2680 {
2681 ++iter;
2682 }
2683 }
2684 }
2685 }
2686 }
2687 else
2688 {
2689 auto result = std::find_if(parentVT->assignedAuxiliaryInputDevices.begin(), parentVT->assignedAuxiliaryInputDevices.end(), [&isoName](const AssignedAuxiliaryInputDevice &aux) {
2690 return aux.name == isoName;
2691 });
2692 if (result != std::end(parentVT->assignedAuxiliaryInputDevices))
2693 {
2694 if (static_cast<std::uint8_t>(AuxiliaryTypeTwoFunctionType::QuadratureBooleanMomentary) >= functionType)
2695 {
2696 AssignedAuxiliaryFunction assignment(functionObjectID, inputObjectID, static_cast<AuxiliaryTypeTwoFunctionType>(functionType));
2697 auto location = std::find(result->functions.begin(), result->functions.end(), assignment);
2698 if (location == std::end(result->functions))
2699 {
2700 result->functions.push_back(assignment);
2701 if (storeAsPreferred)
2702 {
2704 }
2705 LOG_INFO("[AUX-N]: Assigned function " + isobus::to_string(static_cast<int>(functionObjectID)) + " to input " + isobus::to_string(static_cast<int>(inputObjectID)));
2706 }
2707 else
2708 {
2709 hasError = true;
2710 isAlreadyAssigned = true;
2711 LOG_WARNING("[AUX-N]: Unable to store preferred assignment due to missing auxiliary input device with name: " + isobus::to_string(isoName));
2712 }
2713 }
2714 else
2715 {
2716 hasError = true;
2717 LOG_WARNING("[AUX-N]: Unable to store preferred assignment due to unsupported function type: " + isobus::to_string(functionType));
2718 }
2719 }
2720 else
2721 {
2722 hasError = true;
2723 LOG_WARNING("[AUX-N]: Unable to store preferred assignment due to missing auxiliary input device with name: " + isobus::to_string(isoName));
2724 }
2725 }
2726 parentVT->send_auxiliary_function_assignment_response(functionObjectID, hasError, isAlreadyAssigned);
2727 }
2728 else
2729 {
2730 LOG_WARNING("[AUX-N]: Received AuxiliaryAssignmentTypeTwoCommand with wrong data length: " + isobus::to_string(message.get_data_length()) + " but expected 14.");
2731 }
2732 }
2733 break;
2734
2735 case static_cast<std::uint8_t>(Function::AuxiliaryInputTypeTwoStatusMessage):
2736 {
2737 std::uint16_t inputObjectID = message.get_uint16_at(1);
2738 std::uint16_t value1 = message.get_uint16_at(3);
2739 std::uint16_t value2 = message.get_uint16_at(5);
2742 // bool learnModeActive = message.get_bool_at(7, 0);
2743 // bool inputActive = message.get_bool_at(7, 1); // Only in learn mode?
2744 // bool controlIsLocked = false;
2745 // bool interactionWhileLocked = false;
2747 {
2748 // controlIsLocked = message.get_bool_at(7, 2);
2749 // interactionWhileLocked = message.get_bool_at(7, 3);
2750 }
2752 {
2753 auto result = std::find_if(aux.functions.begin(), aux.functions.end(), [&inputObjectID](const AssignedAuxiliaryFunction &assignment) {
2754 return assignment.inputObjectID == inputObjectID;
2755 });
2756 if (aux.functions.end() != result)
2757 {
2758 parentVT->auxiliaryFunctionEventDispatcher.invoke({ *result, parentVT, value1, value2 });
2759 }
2760 }
2761 }
2762 break;
2763
2764 case static_cast<std::uint8_t>(Function::AuxiliaryInputStatusTypeTwoEnableCommand):
2765 {
2766 std::uint16_t inputObjectID = message.get_uint16_at(1);
2767 bool shouldEnable = message.get_bool_at(3, 0);
2768 auto result = std::find_if(parentVT->ourAuxiliaryInputs.begin(), parentVT->ourAuxiliaryInputs.end(), [&inputObjectID](const std::pair<std::uint16_t, AuxiliaryInputState> &input) {
2769 return input.first == inputObjectID;
2770 });
2771 bool isInvalidObjectID = (result == std::end(parentVT->ourAuxiliaryInputs));
2772 if (!isInvalidObjectID)
2773 {
2774 result->second.enabled = shouldEnable;
2775 }
2776 parentVT->send_auxiliary_input_status_enable_response(inputObjectID, isInvalidObjectID ? false : shouldEnable, isInvalidObjectID);
2777 }
2778 break;
2779
2780 case static_cast<std::uint8_t>(Function::VTStatusMessage):
2781 {
2782 parentVT->lastVTStatusTimestamp_ms = SystemTiming::get_timestamp_ms();
2783 parentVT->activeWorkingSetMasterAddress = message.get_uint8_at(1);
2784 parentVT->activeWorkingSetDataMaskObjectID = message.get_uint16_at(2);
2786 parentVT->busyCodesBitfield = message.get_uint8_at(6);
2787 parentVT->currentCommandFunctionCode = message.get_uint8_at(7);
2788 }
2789 break;
2790
2791 case static_cast<std::uint8_t>(Function::GetMemoryMessage):
2792 {
2794 {
2795 parentVT->connectedVTVersion = message.get_uint8_at(1);
2796
2797 if (0 == message.get_uint8_at(2))
2798 {
2799 // There IS enough memory
2801 }
2802 else
2803 {
2805 LOG_ERROR("[VT]: Connection Failed Not Enough Memory");
2806 }
2807 }
2808 }
2809 break;
2810
2811 case static_cast<std::uint8_t>(Function::GetNumberOfSoftKeysMessage):
2812 {
2814 {
2815 parentVT->softKeyXAxisPixels = message.get_uint8_at(4);
2816 parentVT->softKeyYAxisPixels = message.get_uint8_at(5);
2818 parentVT->numberPhysicalSoftkeys = message.get_uint8_at(7);
2820 }
2821 }
2822 break;
2823
2824 case static_cast<std::uint8_t>(Function::GetTextFontDataMessage):
2825 {
2827 {
2828 parentVT->smallFontSizesBitfield = message.get_uint8_at(5);
2829 parentVT->largeFontSizesBitfield = message.get_uint8_at(6);
2830 parentVT->fontStylesBitfield = message.get_uint8_at(7);
2832 }
2833 }
2834 break;
2835
2836 case static_cast<std::uint8_t>(Function::GetHardwareMessage):
2837 {
2839 {
2840 if (message.get_uint8_at(2) <= static_cast<std::uint8_t>(GraphicMode::TwoHundredFiftySixColour))
2841 {
2842 parentVT->supportedGraphicsMode = static_cast<GraphicMode>(message.get_uint8_at(2));
2843 }
2844 parentVT->hardwareFeaturesBitfield = message.get_uint8_at(3);
2845 parentVT->xPixels = message.get_uint16_at(4);
2846 parentVT->yPixels = message.get_uint16_at(6);
2847 parentVT->lastObjectPoolIndex = 0;
2848
2849 // Check if we need to ask for pool versions
2850 // Ony check the first pool, all pools are labeled the same per working set.
2851 if ((!parentVT->objectPools.empty()) &&
2852 (!parentVT->objectPools[0].versionLabel.empty()))
2853 {
2855 }
2856 else
2857 {
2859 }
2860 }
2861 }
2862 break;
2863
2864 case static_cast<std::uint8_t>(Function::GetVersionsResponse):
2865 {
2867 {
2868 // See if the server returned any labels
2869 const std::uint8_t numberOfLabels = message.get_uint8_at(1);
2870 constexpr std::size_t LABEL_LENGTH = 7;
2871
2872 if (numberOfLabels > 0)
2873 {
2874 // Check for label match
2875 bool labelMatched = false;
2876 const std::size_t remainingLength = (2 + (LABEL_LENGTH * numberOfLabels));
2877
2878 if (message.get_data_length() >= remainingLength)
2879 {
2880 for (std::uint_fast8_t i = 0; i < numberOfLabels; i++)
2881 {
2882 char tempStringLabel[8] = { 0 };
2883 tempStringLabel[0] = message.get_uint8_at(2 + (LABEL_LENGTH * i));
2884 tempStringLabel[1] = message.get_uint8_at(3 + (LABEL_LENGTH * i));
2885 tempStringLabel[2] = message.get_uint8_at(4 + (LABEL_LENGTH * i));
2886 tempStringLabel[3] = message.get_uint8_at(5 + (LABEL_LENGTH * i));
2887 tempStringLabel[4] = message.get_uint8_at(6 + (LABEL_LENGTH * i));
2888 tempStringLabel[5] = message.get_uint8_at(7 + (LABEL_LENGTH * i));
2889 tempStringLabel[6] = message.get_uint8_at(8 + (LABEL_LENGTH * i));
2890 tempStringLabel[7] = '\0';
2891 std::string labelDecoded(tempStringLabel);
2892 std::string tempActualLabel(parentVT->objectPools[0].versionLabel);
2893
2894 // Check if we need to manipulate the passed in label by padding with spaces
2895 while (tempActualLabel.size() < LABEL_LENGTH)
2896 {
2897 tempActualLabel.push_back(' ');
2898 }
2899
2900 if (tempActualLabel.size() > LABEL_LENGTH)
2901 {
2902 tempActualLabel.resize(LABEL_LENGTH);
2903 }
2904
2905 if (tempActualLabel == labelDecoded)
2906 {
2907 labelMatched = true;
2909 LOG_INFO("[VT]: VT Server has a matching label for " + isobus::to_string(labelDecoded) + ". It will be loaded and upload will be skipped.");
2910 break;
2911 }
2912 else
2913 {
2914 LOG_INFO("[VT]: VT Server has a label for " + isobus::to_string(labelDecoded) + ". This version will be deleted.");
2915 const std::array<std::uint8_t, 7> deleteBuffer = {
2916 static_cast<std::uint8_t>(labelDecoded[0]),
2917 static_cast<std::uint8_t>(labelDecoded[1]),
2918 static_cast<std::uint8_t>(labelDecoded[2]),
2919 static_cast<std::uint8_t>(labelDecoded[3]),
2920 static_cast<std::uint8_t>(labelDecoded[4]),
2921 static_cast<std::uint8_t>(labelDecoded[5]),
2922 static_cast<std::uint8_t>(labelDecoded[6])
2923 };
2924 if (!parentVT->send_delete_version(deleteBuffer))
2925 {
2926 LOG_WARNING("[VT]: Failed to send the delete version message for label " + isobus::to_string(labelDecoded));
2927 }
2928 }
2929 }
2930 if (!labelMatched)
2931 {
2932 LOG_INFO("[VT]: No version label from the VT matched. Client will upload the pool and store it instead.");
2934 }
2935 }
2936 else
2937 {
2938 LOG_WARNING("[VT]: Get Versions Response length is not long enough. Message ignored.");
2939 }
2940 }
2941 else
2942 {
2943 LOG_INFO("[VT]: No version label from the VT matched. Client will upload the pool and store it instead.");
2945 }
2946 }
2947 else
2948 {
2949 LOG_WARNING("[VT]: Get Versions Response ignored!");
2950 }
2951 }
2952 break;
2953
2954 case static_cast<std::uint8_t>(Function::LoadVersionCommand):
2955 {
2957 {
2958 if (0 == message.get_uint8_at(5))
2959 {
2960 LOG_INFO("[VT]: Loaded object pool version from VT non-volatile memory with no errors.");
2962
2964 if (parentVT->auxiliaryFunctionEventDispatcher.get_listener_count() > 0)
2965 {
2967 {
2968 LOG_DEBUG("[AUX-N]: Sent preferred assignments after LoadVersionCommand.");
2969 }
2970 else
2971 {
2972 LOG_WARNING("[AUX-N]: Failed to send preferred assignments after LoadVersionCommand.");
2973 }
2974 }
2975 }
2976 else
2977 {
2978 // At least one error is set
2979 if (message.get_bool_at(5, 0))
2980 {
2981 LOG_WARNING("[VT]: Load Versions Response error: File system error or corruption.");
2982 }
2983 if (message.get_bool_at(5, 1))
2984 {
2985 LOG_WARNING("[VT]: Load Versions Response error: Insufficient memory.");
2986 }
2987 if (message.get_bool_at(5, 2))
2988 {
2989 LOG_WARNING("[VT]: Load Versions Response error: Any other error.");
2990 }
2991
2992 // Not sure what happened here... should be mostly impossible. Try to upload instead.
2993 LOG_WARNING("[VT]: Switching to pool upload instead.");
2995 }
2996 }
2997 else
2998 {
2999 LOG_WARNING("[VT]: Load Versions Response ignored!");
3000 }
3001 }
3002 break;
3003
3004 case static_cast<std::uint8_t>(Function::StoreVersionCommand):
3005 {
3007 {
3008 if (0 == message.get_uint8_at(5))
3009 {
3010 // Stored with no error
3012 LOG_INFO("[VT]: Stored object pool with no error.");
3013 }
3014 else
3015 {
3016 // At least one error is set
3017 if (message.get_bool_at(5, 0))
3018 {
3019 LOG_WARNING("[VT]: Store Versions Response error: Version label is not correct.");
3020 }
3021 if (message.get_bool_at(5, 1))
3022 {
3023 LOG_WARNING("[VT]: Store Versions Response error: Insufficient memory.");
3024 }
3025 if (message.get_bool_at(5, 2))
3026 {
3027 LOG_WARNING("[VT]: Store Versions Response error: Any other error.");
3028 }
3029 }
3030 }
3031 else
3032 {
3033 LOG_WARNING("[VT]: Store Versions Response ignored!");
3034 }
3035 }
3036 break;
3037
3038 case static_cast<std::uint8_t>(Function::DeleteVersionCommand):
3039 {
3040 if (0 == message.get_uint8_at(5))
3041 {
3042 LOG_INFO("[VT]: Delete Version Response OK!");
3043 }
3044 else
3045 {
3046 if (message.get_bool_at(5, 1))
3047 {
3048 LOG_WARNING("[VT]: Delete Version Response error: Version label is not correct, or unknown.");
3049 }
3050 if (message.get_bool_at(5, 3))
3051 {
3052 LOG_WARNING("[VT]: Delete Version Response error: Any other error.");
3053 }
3054 }
3055 }
3056 break;
3057
3058 case static_cast<std::uint8_t>(Function::EndOfObjectPoolMessage):
3059 {
3061 {
3062 bool anyErrorInPool = message.get_bool_at(1, 0);
3063 bool vtRanOutOfMemory = message.get_bool_at(1, 1);
3064 bool otherErrors = message.get_bool_at(1, 3);
3065 std::uint16_t parentObjectIDOfFaultyObject = message.get_uint16_at(2);
3066 std::uint16_t objectIDOfFaultyObject = message.get_uint16_at(4);
3067 std::uint8_t objectPoolErrorBitmask = message.get_uint8_at(6);
3068
3069 if ((!anyErrorInPool) &&
3070 (0 == objectPoolErrorBitmask))
3071 {
3072 // Clear scaling buffers
3073 for (auto &objectPool : parentVT->objectPools)
3074 {
3075 objectPool.scaledObjectPool.clear();
3076 }
3077
3078 // Check if we need to store this pool
3079 if (!parentVT->objectPools[0].versionLabel.empty())
3080 {
3082 }
3083 else
3084 {
3086 }
3088 if (parentVT->auxiliaryFunctionEventDispatcher.get_listener_count() > 0)
3089 {
3091 {
3092 LOG_DEBUG("[AUX-N]: Sent preferred assignments after EndOfObjectPoolMessage.");
3093 }
3094 else
3095 {
3096 LOG_WARNING("[AUX-N]: Failed to send preferred assignments after EndOfObjectPoolMessage.");
3097 }
3098 }
3099 }
3100 else
3101 {
3103 LOG_ERROR("[VT]: Error in end of object pool message." +
3104 std::string("Faulty Object ") +
3105 isobus::to_string(static_cast<int>(objectIDOfFaultyObject)) +
3106 std::string(" Faulty Object Parent ") +
3107 isobus::to_string(static_cast<int>(parentObjectIDOfFaultyObject)) +
3108 std::string(" Pool error bitmask value ") +
3109 isobus::to_string(static_cast<int>(objectPoolErrorBitmask)));
3110 if (vtRanOutOfMemory)
3111 {
3112 LOG_ERROR("[VT]: Ran out of memory");
3113 }
3114 if (otherErrors)
3115 {
3116 LOG_ERROR("[VT]: Reported other errors in EOM response");
3117 }
3118 }
3119 }
3120 }
3121 break;
3122
3123 case static_cast<std::uint8_t>(Function::HideShowObjectCommand):
3124 case static_cast<std::uint8_t>(Function::EnableDisableObjectCommand):
3125 case static_cast<std::uint8_t>(Function::SelectInputObjectCommand):
3126 case static_cast<std::uint8_t>(Function::ESCCommand):
3127 case static_cast<std::uint8_t>(Function::ControlAudioSignalCommand):
3128 case static_cast<std::uint8_t>(Function::SetAudioVolumeCommand):
3129 case static_cast<std::uint8_t>(Function::ChangeChildLocationCommand):
3130 case static_cast<std::uint8_t>(Function::ChangeChildPositionCommand):
3131 case static_cast<std::uint8_t>(Function::ChangeSizeCommand):
3132 case static_cast<std::uint8_t>(Function::ChangeBackgroundColourCommand):
3133 case static_cast<std::uint8_t>(Function::ChangeNumericValueCommand):
3134 case static_cast<std::uint8_t>(Function::ChangeStringValueCommand):
3135 case static_cast<std::uint8_t>(Function::ChangeEndPointCommand):
3136 case static_cast<std::uint8_t>(Function::ChangeFontAttributesCommand):
3137 case static_cast<std::uint8_t>(Function::ChangeLineAttributesCommand):
3138 case static_cast<std::uint8_t>(Function::ChangeFillAttributesCommand):
3139 case static_cast<std::uint8_t>(Function::ChangeActiveMaskCommand):
3140 case static_cast<std::uint8_t>(Function::ChangeSoftKeyMaskCommand):
3141 case static_cast<std::uint8_t>(Function::ChangeAttributeCommand):
3142 case static_cast<std::uint8_t>(Function::ChangePriorityCommand):
3143 case static_cast<std::uint8_t>(Function::ChangeListItemCommand):
3144 case static_cast<std::uint8_t>(Function::DeleteObjectPoolCommand):
3145 case static_cast<std::uint8_t>(Function::LockUnlockMaskCommand):
3146 case static_cast<std::uint8_t>(Function::ExecuteMacroCommand):
3147 case static_cast<std::uint8_t>(Function::ChangeObjectLabelCommand):
3148 case static_cast<std::uint8_t>(Function::ChangePolygonPointCommand):
3149 case static_cast<std::uint8_t>(Function::ChangePolygonScaleCommand):
3150 case static_cast<std::uint8_t>(Function::GraphicsContextCommand):
3151 case static_cast<std::uint8_t>(Function::GetAttributeValueMessage):
3152 case static_cast<std::uint8_t>(Function::SelectColourMapCommand):
3153 case static_cast<std::uint8_t>(Function::ExecuteExtendedMacroCommand):
3154 case static_cast<std::uint8_t>(Function::SelectActiveWorkingSet):
3155 {
3156 // By checking if it's a response with our control functions, we verify that it's a response to a request we sent.
3157 // This because we only support Working Set Masters at the moment.
3158 if ((parentVT->myControlFunction == message.get_destination_control_function()) &&
3159 (parentVT->partnerControlFunction == message.get_source_control_function()))
3160 {
3161 parentVT->commandAwaitingResponse = false;
3162 parentVT->process_command_queue();
3163 }
3164 }
3165 break;
3166 case static_cast<std::uint8_t>(Function::UnsupportedVTFunctionMessage):
3167 {
3168 std::uint8_t unsupportedFunction = message.get_uint8_at(1);
3169 if (std::find(parentVT->unsupportedFunctions.begin(), parentVT->unsupportedFunctions.end(), unsupportedFunction) == parentVT->unsupportedFunctions.end())
3170 {
3171 parentVT->unsupportedFunctions.push_back(unsupportedFunction);
3172 }
3173 LOG_WARNING("[VT]: Server indicated VT Function '%llu' is unsupported, caching it", unsupportedFunction);
3174 }
3175 break;
3176 default:
3177 {
3178 std::uint8_t unsupportedFunction = message.get_uint8_at(0);
3179 LOG_WARNING("[VT]: Server sent function '%llu' which we do not support", unsupportedFunction);
3180 std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
3181 static_cast<std::uint8_t>(Function::UnsupportedVTFunctionMessage),
3182 unsupportedFunction,
3183 0xFF,
3184 0xFF,
3185 0xFF,
3186 0xFF,
3187 0xFF,
3188 0xFF,
3189 };
3190 parentVT->send_message_to_vt(buffer.data(), buffer.size());
3191 }
3192 break;
3193 }
3194 }
3195 break;
3196
3197 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::ECUtoVirtualTerminal):
3198 {
3199 switch (message.get_uint8_at(0))
3200 {
3201 case static_cast<std::uint8_t>(Function::AuxiliaryInputTypeTwoMaintenanceMessage):
3202 {
3203 std::uint16_t modelIdentificationCode = message.get_uint16_at(1);
3204 bool ready = message.get_uint8_at(3);
3205
3206 if (ready)
3207 {
3208 auto result = std::find_if(parentVT->assignedAuxiliaryInputDevices.begin(), parentVT->assignedAuxiliaryInputDevices.end(), [&modelIdentificationCode](const AssignedAuxiliaryInputDevice &aux) {
3209 return aux.modelIdentificationCode == modelIdentificationCode;
3210 });
3211 if (result == std::end(parentVT->assignedAuxiliaryInputDevices))
3212 {
3213 AssignedAuxiliaryInputDevice inputDevice{ message.get_source_control_function()->get_NAME().get_full_name(), modelIdentificationCode, {} };
3214 parentVT->assignedAuxiliaryInputDevices.push_back(inputDevice);
3215 LOG_INFO("[AUX-N]: New auxiliary input device with name: " +
3216 isobus::to_string(inputDevice.name) +
3217 " and model identification code: " +
3218 isobus::to_string(modelIdentificationCode));
3219 }
3220 }
3221 }
3222 break;
3223 }
3224 }
3225 break;
3226
3227 default:
3228 {
3229 LOG_WARNING("[VT]: Client unknown message: " + isobus::to_string(static_cast<int>(message.get_identifier().get_parameter_group_number())));
3230 }
3231 break;
3232 }
3233 }
3234 else
3235 {
3236 LOG_WARNING("[VT]: VT-ECU Client message invalid");
3237 }
3238 }
3239
3240 void VirtualTerminalClient::process_callback(std::uint32_t parameterGroupNumber,
3241 std::uint32_t,
3242 std::shared_ptr<InternalControlFunction>,
3243 std::shared_ptr<ControlFunction> destinationControlFunction,
3244 bool successful,
3245 void *parentPointer)
3246 {
3247 if ((nullptr != parentPointer) &&
3248 (static_cast<std::uint32_t>(CANLibParameterGroupNumber::ECUtoVirtualTerminal) == parameterGroupNumber) &&
3249 (nullptr != destinationControlFunction))
3250 {
3251 VirtualTerminalClient *parent = static_cast<VirtualTerminalClient *>(parentPointer);
3252
3254 {
3255 if (successful)
3256 {
3258 }
3259 else
3260 {
3262 }
3263 }
3264 }
3265 }
3266
3268 std::uint32_t bytesOffset,
3269 std::uint32_t numberOfBytesNeeded,
3270 std::uint8_t *chunkBuffer,
3271 void *parentPointer)
3272 {
3273 bool retVal = false;
3274
3275 if ((nullptr != parentPointer) &&
3276 (nullptr != chunkBuffer) &&
3277 (0 != numberOfBytesNeeded))
3278 {
3279 VirtualTerminalClient *parentVTClient = static_cast<VirtualTerminalClient *>(parentPointer);
3280 std::uint32_t poolIndex = std::numeric_limits<std::uint32_t>::max();
3281 bool usingExternalCallback = false;
3282
3283 // Need to figure out which pool we're currently uploading
3284 for (std::uint32_t i = 0; i < parentVTClient->objectPools.size(); i++)
3285 {
3286 if (!parentVTClient->objectPools[i].uploaded)
3287 {
3288 poolIndex = i;
3289 usingExternalCallback = parentVTClient->objectPools[i].useDataCallback;
3290 break;
3291 }
3292 }
3293
3294 // If pool index is FFs, something is wrong with the state machine state, return false.
3295 if ((std::numeric_limits<std::uint32_t>::max() != poolIndex) &&
3296 (bytesOffset + numberOfBytesNeeded) <= parentVTClient->objectPools[poolIndex].objectPoolSize + 1)
3297 {
3298 // We've got more data to transfer
3299 if ((0 != parentVTClient->objectPools[poolIndex].autoScaleDataMaskOriginalDimension) && (0 != parentVTClient->objectPools[poolIndex].autoScaleSoftKeyDesignatorOriginalHeight))
3300 {
3301 // Object pool has been pre-scaled. Use the scaling buffer instead
3302 retVal = true;
3303 if (0 == bytesOffset)
3304 {
3305 chunkBuffer[0] = static_cast<std::uint8_t>(Function::ObjectPoolTransferMessage);
3306 memcpy(&chunkBuffer[1], &parentVTClient->objectPools[poolIndex].scaledObjectPool[bytesOffset], numberOfBytesNeeded - 1);
3307 }
3308 else
3309 {
3310 // Subtract off 1 to account for the mux in the first byte of the message
3311 memcpy(chunkBuffer, &parentVTClient->objectPools[poolIndex].scaledObjectPool[bytesOffset - 1], numberOfBytesNeeded);
3312 }
3313 }
3314 else
3315 {
3316 if (usingExternalCallback)
3317 {
3318 // We're using the user's supplied callback to get a chunk of info
3319 if (0 == bytesOffset)
3320 {
3321 chunkBuffer[0] = static_cast<std::uint8_t>(Function::ObjectPoolTransferMessage);
3322 retVal = parentVTClient->objectPools[poolIndex].dataCallback(callbackIndex, bytesOffset, numberOfBytesNeeded - 1, &chunkBuffer[1], parentVTClient);
3323 }
3324 else
3325 {
3326 // Subtract off 1 to account for the mux in the first byte of the message
3327 retVal = parentVTClient->objectPools[poolIndex].dataCallback(callbackIndex, bytesOffset - 1, numberOfBytesNeeded, chunkBuffer, parentVTClient);
3328 }
3329 }
3330 else
3331 {
3332 // We already have the whole pool in RAM
3333 retVal = true;
3334 if (0 == bytesOffset)
3335 {
3336 chunkBuffer[0] = static_cast<std::uint8_t>(Function::ObjectPoolTransferMessage);
3337 memcpy(&chunkBuffer[1], &parentVTClient->objectPools[poolIndex].objectPoolDataPointer[bytesOffset], numberOfBytesNeeded - 1);
3338 }
3339 else
3340 {
3341 // Subtract off 1 to account for the mux in the first byte of the message
3342 memcpy(chunkBuffer, &parentVTClient->objectPools[poolIndex].objectPoolDataPointer[bytesOffset - 1], numberOfBytesNeeded);
3343 }
3344 }
3345 }
3346 }
3347 }
3348 return retVal;
3349 }
3350
3352 {
3353 bool retVal = false;
3354
3355 for (auto &objectPool : objectPools)
3356 {
3357 if ((0 != objectPool.autoScaleDataMaskOriginalDimension) &&
3358 (0 != objectPool.autoScaleSoftKeyDesignatorOriginalHeight))
3359 {
3360 retVal = true;
3361 break;
3362 }
3363 }
3364 return retVal;
3365 }
3366
3368 {
3369 bool retVal = true;
3370
3371 for (auto &objectPool : objectPools)
3372 {
3373 // Step 1: Make a read/write copy of the pool
3374 if (nullptr != objectPool.objectPoolDataPointer)
3375 {
3376 objectPool.scaledObjectPool.resize(objectPool.objectPoolSize);
3377 memcpy(&objectPool.scaledObjectPool[0], objectPool.objectPoolDataPointer, objectPool.objectPoolSize);
3378 }
3379 else if (nullptr != objectPool.objectPoolVectorPointer)
3380 {
3381 objectPool.scaledObjectPool.resize(objectPool.objectPoolVectorPointer->size());
3382 std::copy(objectPool.objectPoolVectorPointer->begin(), objectPool.objectPoolVectorPointer->end(), objectPool.scaledObjectPool.begin());
3383 }
3384 else if (objectPool.useDataCallback)
3385 {
3386 objectPool.scaledObjectPool.resize(objectPool.objectPoolSize);
3387
3388 for (std::uint32_t i = 0; i < objectPool.objectPoolSize; i++)
3389 {
3390 retVal &= objectPool.dataCallback(i, i, 1, &objectPool.scaledObjectPool[i], this);
3391 }
3392
3393 if (!retVal)
3394 {
3395 break;
3396 }
3397 }
3398
3399 // Step 2, Parse the pool and resize each object as we iterate through it
3400 auto poolIterator = objectPool.scaledObjectPool.begin();
3401
3402 while ((poolIterator != objectPool.scaledObjectPool.end()) &&
3403 retVal)
3404 {
3405 if (VirtualTerminalObjectType::Key == static_cast<VirtualTerminalObjectType>(poolIterator[2]))
3406 {
3407 retVal &= resize_object(&poolIterator[0],
3408 static_cast<float>(get_softkey_x_axis_pixels()) / static_cast<float>(objectPool.autoScaleSoftKeyDesignatorOriginalHeight),
3409 static_cast<VirtualTerminalObjectType>(poolIterator[2]));
3410 }
3411 else
3412 {
3413 retVal &= resize_object(&poolIterator[0],
3414 static_cast<float>(get_number_x_pixels()) / static_cast<float>(objectPool.autoScaleDataMaskOriginalDimension),
3415 static_cast<VirtualTerminalObjectType>(poolIterator[2]));
3416 }
3417
3418 std::uint32_t objectSize = get_number_bytes_in_object(&poolIterator[0]);
3419 if (retVal)
3420 {
3421 if (get_is_object_scalable(static_cast<VirtualTerminalObjectType>(*(poolIterator + 2))))
3422 {
3423 LOG_DEBUG("[VT]: Resized an object: " +
3424 isobus::to_string(static_cast<int>((*poolIterator)) | (static_cast<int>((*poolIterator + 1))) << 8) +
3425 " with type " +
3426 isobus::to_string(static_cast<int>((*(poolIterator + 2)))) +
3427 " with size " +
3428 isobus::to_string(static_cast<int>(objectSize)));
3429 }
3430 }
3431 else
3432 {
3433 LOG_ERROR("[VT]: Failed to resize an object: " +
3434 isobus::to_string(static_cast<int>((*poolIterator)) | (static_cast<int>((*poolIterator + 1))) << 8) +
3435 " with type " +
3436 isobus::to_string(static_cast<int>((*poolIterator + 2))) +
3437 " with size " +
3438 isobus::to_string(static_cast<int>(objectSize)));
3439 }
3440 poolIterator += objectSize;
3441 }
3442 }
3443 return retVal;
3444 }
3445
3447 {
3448 bool retVal = false;
3449
3450 switch (type)
3451 {
3453 {
3454 retVal = false;
3455 }
3456 break;
3457
3483 {
3484 retVal = true;
3485 }
3486 break;
3487
3488 default:
3489 {
3490 retVal = false;
3491 }
3492 break;
3493 }
3494 return retVal;
3495 }
3496
3498 {
3499 FontSize retVal = originalFont;
3500
3501 while ((!get_font_size_supported(retVal)) && (FontSize::Size6x8 != retVal))
3502 {
3503 retVal = static_cast<FontSize>(static_cast<std::uint8_t>(originalFont) - 1);
3504 }
3505 return retVal;
3506 }
3507
3509 {
3510 static constexpr float SCALE_FACTOR_POSITIVE_FUDGE = 1.05f;
3511 static constexpr float SCALE_FACTOR_NEGATIVE_FUDGE = 0.95f;
3512 FontSize retVal = originalFont;
3513
3514 if (scaleFactor > SCALE_FACTOR_POSITIVE_FUDGE || scaleFactor < SCALE_FACTOR_NEGATIVE_FUDGE)
3515 {
3516 const std::unordered_map<FontSize, std::map<float, FontSize, std::greater<float>>> FONT_SCALING_MAPPER{
3518 { { 23.95f, FontSize::Size128x192 },
3519 { 21.30f, FontSize::Size128x128 },
3520 { 15.95f, FontSize::Size96x128 },
3521 { 11.95f, FontSize::Size64x96 },
3522 { 10.60f, FontSize::Size64x64 },
3523 { 7.95f, FontSize::Size48x64 },
3524 { 5.95f, FontSize::Size32x48 },
3525 { 5.30f, FontSize::Size32x32 },
3526 { 3.95f, FontSize::Size24x32 },
3527 { 2.95f, FontSize::Size16x24 },
3528 { 2.60f, FontSize::Size16x16 },
3529 { 1.95f, FontSize::Size12x16 },
3530 { 1.45f, FontSize::Size8x12 },
3531 { 1.30f, FontSize::Size8x8 } } },
3533 { { 23.95f, FontSize::Size128x192 },
3534 { 15.95f, FontSize::Size128x128 },
3535 { 11.95f, FontSize::Size64x96 },
3536 { 7.95f, FontSize::Size64x64 },
3537 { 5.95f, FontSize::Size32x48 },
3538 { 3.95f, FontSize::Size32x32 },
3539 { 2.95f, FontSize::Size16x24 },
3540 { 1.95f, FontSize::Size16x16 },
3541 { 1.45f, FontSize::Size8x12 },
3542 { 0.0f, FontSize::Size6x8 } } },
3544 { { 15.95f, FontSize::Size128x192 },
3545 { 11.95f, FontSize::Size96x128 },
3546 { 7.95f, FontSize::Size64x96 },
3547 { 5.95f, FontSize::Size48x64 },
3548 { 3.95f, FontSize::Size32x48 },
3549 { 2.95f, FontSize::Size24x32 },
3550 { 1.95f, FontSize::Size16x24 },
3551 { 1.45f, FontSize::Size12x16 },
3552 { 0.0f, FontSize::Size6x8 } } },
3554 { { 11.95f, FontSize::Size128x192 },
3555 { 10.60f, FontSize::Size128x128 },
3556 { 7.95f, FontSize::Size96x128 },
3557 { 5.95f, FontSize::Size64x96 },
3558 { 5.30f, FontSize::Size64x64 },
3559 { 3.95f, FontSize::Size48x64 },
3560 { 2.95f, FontSize::Size32x48 },
3561 { 2.60f, FontSize::Size32x32 },
3562 { 1.95f, FontSize::Size24x32 },
3563 { 1.45f, FontSize::Size16x24 },
3564 { 1.30f, FontSize::Size16x16 },
3565 { 0.75f, FontSize::Size8x12 },
3566 { 0.67f, FontSize::Size8x8 },
3567 { 0.0f, FontSize::Size6x8 } } },
3569 { { 11.95f, FontSize::Size128x192 },
3570 { 7.95f, FontSize::Size128x128 },
3571 { 5.95f, FontSize::Size64x96 },
3572 { 3.95f, FontSize::Size64x64 },
3573 { 2.95f, FontSize::Size32x48 },
3574 { 1.95f, FontSize::Size32x32 },
3575 { 1.45f, FontSize::Size16x24 },
3576 { 0.75f, FontSize::Size8x12 },
3577 { 0.50f, FontSize::Size8x8 },
3578 { 0.0f, FontSize::Size6x8 } } },
3580 { { 7.95f, FontSize::Size128x128 },
3581 { 5.95f, FontSize::Size96x128 },
3582 { 3.95f, FontSize::Size64x96 },
3583 { 2.95f, FontSize::Size48x64 },
3584 { 1.95f, FontSize::Size32x48 },
3585 { 1.45f, FontSize::Size24x32 },
3586 { 0.75f, FontSize::Size12x16 },
3587 { 0.50f, FontSize::Size8x12 },
3588 { 0.0f, FontSize::Size6x8 } } },
3590 { { 5.95f, FontSize::Size128x192 },
3591 { 5.30f, FontSize::Size128x128 },
3592 { 3.95f, FontSize::Size96x128 },
3593 { 2.95f, FontSize::Size64x96 },
3594 { 2.60f, FontSize::Size64x64 },
3595 { 1.95f, FontSize::Size48x64 },
3596 { 1.45f, FontSize::Size32x48 },
3597 { 1.30f, FontSize::Size32x32 },
3598 { 0.75f, FontSize::Size16x24 },
3599 { 0.60f, FontSize::Size16x16 },
3600 { 0.50f, FontSize::Size12x16 },
3601 { 0.37f, FontSize::Size8x12 },
3602 { 0.30f, FontSize::Size8x8 },
3603 { 0.0f, FontSize::Size6x8 } } },
3605 { { 5.95f, FontSize::Size128x192 },
3606 { 3.95f, FontSize::Size128x128 },
3607 { 2.95f, FontSize::Size64x96 },
3608 { 1.95f, FontSize::Size64x64 },
3609 { 1.45f, FontSize::Size32x48 },
3610 { 0.75f, FontSize::Size16x24 },
3611 { 0.50f, FontSize::Size16x16 },
3612 { 0.37f, FontSize::Size8x12 },
3613 { 0.20f, FontSize::Size8x8 },
3614 { 0.0f, FontSize::Size6x8 } } },
3616 { { 3.95f, FontSize::Size128x192 },
3617 { 2.95f, FontSize::Size96x128 },
3618 { 1.95f, FontSize::Size64x96 },
3619 { 1.45f, FontSize::Size48x64 },
3620 { 0.75f, FontSize::Size24x32 },
3621 { 0.50f, FontSize::Size16x24 },
3622 { 0.37f, FontSize::Size12x16 },
3623 { 0.20f, FontSize::Size8x12 },
3624 { 0.0f, FontSize::Size6x8 } } },
3626 { { 2.95f, FontSize::Size128x192 },
3627 { 2.60f, FontSize::Size128x128 },
3628 { 1.95f, FontSize::Size96x128 },
3629 { 1.45f, FontSize::Size64x96 },
3630 { 1.30f, FontSize::Size64x64 },
3631 { 0.75f, FontSize::Size32x48 },
3632 { 0.60f, FontSize::Size32x32 },
3633 { 0.50f, FontSize::Size24x32 },
3634 { 0.37f, FontSize::Size16x24 },
3635 { 0.33f, FontSize::Size16x16 },
3636 { 0.25f, FontSize::Size12x16 },
3637 { 0.18f, FontSize::Size8x12 },
3638 { 0.16f, FontSize::Size8x8 },
3639 { 0.0f, FontSize::Size6x8 } } },
3641 { { 2.95f, FontSize::Size128x192 },
3642 { 1.95f, FontSize::Size128x128 },
3643 { 1.45f, FontSize::Size64x96 },
3644 { 0.75f, FontSize::Size32x48 },
3645 { 0.50f, FontSize::Size32x32 },
3646 { 0.37f, FontSize::Size16x24 },
3647 { 0.25f, FontSize::Size16x16 },
3648 { 0.18f, FontSize::Size8x12 },
3649 { 0.12f, FontSize::Size8x8 },
3650 { 0.0f, FontSize::Size6x8 } } },
3652 { { 1.95f, FontSize::Size128x192 },
3653 { 1.45f, FontSize::Size96x128 },
3654 { 0.75f, FontSize::Size48x64 },
3655 { 0.50f, FontSize::Size32x48 },
3656 { 0.37f, FontSize::Size24x32 },
3657 { 0.25f, FontSize::Size16x24 },
3658 { 0.18f, FontSize::Size12x16 },
3659 { 0.12f, FontSize::Size8x12 },
3660 { 0.0f, FontSize::Size6x8 } } },
3662 { { 1.45f, FontSize::Size128x192 },
3663 { 1.30f, FontSize::Size128x128 },
3664 { 0.75f, FontSize::Size64x96 },
3665 { 0.60f, FontSize::Size64x64 },
3666 { 0.50f, FontSize::Size48x64 },
3667 { 0.37f, FontSize::Size32x48 },
3668 { 0.33f, FontSize::Size32x32 },
3669 { 0.25f, FontSize::Size24x32 },
3670 { 0.18f, FontSize::Size16x24 },
3671 { 0.16f, FontSize::Size16x16 },
3672 { 0.125f, FontSize::Size12x16 },
3673 { 0.09f, FontSize::Size8x12 },
3674 { 0.08f, FontSize::Size8x8 },
3675 { 0.0f, FontSize::Size6x8 } } },
3677 { { 1.45f, FontSize::Size128x192 },
3678 { 0.75f, FontSize::Size64x96 },
3679 { 0.50f, FontSize::Size64x64 },
3680 { 0.37f, FontSize::Size32x48 },
3681 { 0.25f, FontSize::Size32x32 },
3682 { 0.18f, FontSize::Size16x24 },
3683 { 0.125f, FontSize::Size16x16 },
3684 { 0.09f, FontSize::Size8x12 },
3685 { 0.06f, FontSize::Size8x8 },
3686 { 0.0f, FontSize::Size6x8 } } },
3688 { { 0.75f, FontSize::Size96x128 },
3689 { 0.50f, FontSize::Size64x96 },
3690 { 0.37f, FontSize::Size48x64 },
3691 { 0.25f, FontSize::Size32x48 },
3692 { 0.18f, FontSize::Size24x32 },
3693 { 0.125f, FontSize::Size16x24 },
3694 { 0.09f, FontSize::Size12x16 },
3695 { 0.06f, FontSize::Size8x12 },
3696 { 0.0f, FontSize::Size6x8 } } }
3697 };
3698
3699 auto iterator = FONT_SCALING_MAPPER.find(originalFont);
3700 if (iterator != FONT_SCALING_MAPPER.end())
3701 {
3702 for (auto &pair : iterator->second)
3703 {
3704 if (scaleFactor >= pair.first)
3705 {
3706 retVal = pair.second;
3707 break;
3708 }
3709 }
3710 }
3711
3712 if (retVal == originalFont)
3713 {
3714 // Unknown font? Newer version than we support of the ISO standard? Or scaling factor out of range?
3715 LOG_ERROR("[VT]: Unable to scale font type " + isobus::to_string(static_cast<int>(originalFont)) +
3716 " with scale factor " + isobus::to_string(scaleFactor) + ". Returning original font.");
3717 }
3718 }
3719 return retVal;
3720 }
3721
3723 {
3724 std::uint32_t retVal = 0;
3725
3726 switch (type)
3727 {
3729 {
3730 retVal = 10;
3731 }
3732 break;
3733
3737 {
3738 retVal = 12;
3739 }
3740 break;
3741
3745 {
3746 retVal = 10;
3747 }
3748 break;
3749
3751 {
3752 retVal = 9;
3753 }
3754 break;
3755
3758 {
3759 retVal = 6;
3760 }
3761 break;
3762
3766 {
3767 retVal = 7;
3768 }
3769 break;
3770
3776 {
3777 retVal = 13;
3778 }
3779 break;
3780
3782 {
3783 retVal = 19;
3784 }
3785 break;
3786
3788 {
3789 retVal = 38;
3790 }
3791 break;
3792
3794 {
3795 retVal = 17;
3796 }
3797 break;
3798
3800 {
3801 retVal = 29;
3802 }
3803 break;
3804
3806 {
3807 retVal = 11;
3808 }
3809 break;
3810
3812 {
3813 retVal = 15;
3814 }
3815 break;
3816
3818 {
3819 retVal = 14;
3820 }
3821 break;
3822
3824 {
3825 retVal = 21;
3826 }
3827 break;
3828
3830 {
3831 retVal = 24;
3832 }
3833 break;
3834
3836 {
3837 retVal = 27;
3838 }
3839 break;
3840
3844 {
3845 retVal = 17;
3846 }
3847 break;
3848
3853 {
3854 retVal = 5;
3855 }
3856 break;
3857
3862 {
3863 retVal = 8;
3864 }
3865 break;
3866
3868 {
3869 retVal = 34;
3870 }
3871 break;
3872
3873 default:
3874 {
3875 LOG_ERROR("[VT]: Cannot autoscale object pool due to unknown object minimum length - type " + isobus::to_string(static_cast<int>(type)));
3876 }
3877 break;
3878 }
3879 return retVal;
3880 }
3881
3882 std::uint32_t VirtualTerminalClient::get_number_bytes_in_object(std::uint8_t *buffer)
3883 {
3884 auto currentObjectType = static_cast<VirtualTerminalObjectType>(buffer[2]);
3885 std::uint32_t retVal = get_minimum_object_length(currentObjectType);
3886
3887 switch (currentObjectType)
3888 {
3890 {
3891 const std::uint32_t sizeOfChildObjects = (buffer[7] * 6);
3892 const std::uint32_t sizeOfMacros = (buffer[8] * 2);
3893 const std::uint32_t sizeOfLanguageCodes = (buffer[9] * 2);
3894 retVal += (sizeOfLanguageCodes + sizeOfChildObjects + sizeOfMacros);
3895 }
3896 break;
3897
3899 {
3900 const std::uint32_t sizeOfChildObjects = (buffer[6] * 6);
3901 const std::uint32_t sizeOfMacros = (buffer[7] * 2);
3902 retVal += (sizeOfChildObjects + sizeOfMacros);
3903 }
3904 break;
3905
3908 {
3909 const std::uint32_t sizeOfChildObjects = (buffer[8] * 6);
3910 const std::uint32_t sizeOfMacros = (buffer[9] * 2);
3911 retVal += (sizeOfChildObjects + sizeOfMacros);
3912 }
3913 break;
3914
3916 {
3917 const std::uint32_t sizeOfChildObjects = (buffer[4] * 2);
3918 const std::uint32_t sizeOfMacros = (buffer[5] * 2);
3919 retVal += (sizeOfChildObjects + sizeOfMacros);
3920 }
3921 break;
3922
3924 {
3925 const std::uint32_t sizeOfChildObjects = (buffer[5] * 6);
3926 const std::uint32_t sizeOfMacros = (buffer[6] * 2);
3927 retVal += (sizeOfChildObjects + sizeOfMacros);
3928 }
3929 break;
3930
3932 {
3933 const std::uint32_t sizeOfChildObjects = (buffer[11] * 6);
3934 const std::uint32_t sizeOfMacros = (buffer[12] * 2);
3935 retVal += (sizeOfChildObjects + sizeOfMacros);
3936 }
3937 break;
3938
3940 {
3941 const std::uint32_t sizeOfMacros = (buffer[12] * 2);
3942 retVal += sizeOfMacros;
3943 }
3944 break;
3945
3947 {
3948 const std::uint32_t sizeOfValue = buffer[16];
3949 const std::uint32_t sizeOfMacros = (buffer[18 + sizeOfValue] * 2);
3950 retVal += (sizeOfValue + sizeOfMacros);
3951 }
3952 break;
3953
3955 {
3956 const std::uint32_t sizeOfMacros = (buffer[37] * 2);
3957 retVal += sizeOfMacros;
3958 }
3959 break;
3960
3962 {
3963 const std::uint32_t sizeOfMacros = (buffer[12] * 2);
3964 const std::uint32_t sizeOfListObjectIDs = (buffer[10] * 2);
3965 retVal += (sizeOfMacros + sizeOfListObjectIDs);
3966 }
3967 break;
3968
3970 {
3971 const std::uint32_t sizeOfValue = (static_cast<uint16_t>(buffer[14]) | static_cast<uint16_t>(buffer[15] << 8));
3972 const std::uint32_t sizeOfMacros = (buffer[16 + sizeOfValue] * 2);
3973 retVal += (sizeOfMacros + sizeOfValue);
3974 }
3975 break;
3976
3978 {
3979 const std::uint32_t sizeOfMacros = (buffer[28] * 2);
3980 retVal += sizeOfMacros;
3981 }
3982 break;
3983
3985 {
3986 const std::uint32_t sizeOfMacros = (buffer[11] * 2);
3987 const std::uint32_t sizeOfListObjectIDs = (buffer[10] * 2);
3988 retVal += (sizeOfMacros + sizeOfListObjectIDs);
3989 }
3990 break;
3991
3993 {
3994 const std::uint32_t sizeOfMacros = (buffer[10] * 2);
3995 retVal += sizeOfMacros;
3996 }
3997 break;
3998
4000 {
4001 const std::uint32_t sizeOfMacros = (buffer[12] * 2);
4002 retVal += sizeOfMacros;
4003 }
4004 break;
4005
4007 {
4008 const std::uint32_t sizeOfMacros = (buffer[14] * 2);
4009 retVal += sizeOfMacros;
4010 }
4011 break;
4012
4014 {
4015 const std::uint32_t sizeOfPoints = (buffer[12] * 4);
4016 const std::uint32_t sizeOfMacros = (buffer[13] * 2);
4017 retVal += (sizeOfMacros + sizeOfPoints);
4018 }
4019 break;
4020
4022 {
4023 const std::uint32_t sizeOfMacros = (buffer[20] * 2);
4024 retVal += sizeOfMacros;
4025 }
4026 break;
4027
4029 {
4030 const std::uint32_t sizeOfMacros = (buffer[23] * 2);
4031 retVal += sizeOfMacros;
4032 }
4033 break;
4034
4036 {
4037 const std::uint32_t sizeOfMacros = (buffer[26] * 2);
4038 retVal += sizeOfMacros;
4039 }
4040 break;
4041
4043 {
4044 const std::uint32_t sizeOfMacros = (buffer[16] * 2);
4045 const std::uint32_t sizeOfRawData = (static_cast<std::uint32_t>(buffer[12]) |
4046 (static_cast<std::uint32_t>(buffer[13]) << 8) |
4047 (static_cast<std::uint32_t>(buffer[14]) << 16) |
4048 (static_cast<std::uint32_t>(buffer[15]) << 24));
4049 retVal += (sizeOfRawData + sizeOfMacros);
4050 }
4051 break;
4052
4059 {
4060 // No additional length
4061 }
4062 break;
4063
4065 {
4066 const std::uint32_t sizeOfValue = (static_cast<uint16_t>(buffer[3]) | static_cast<uint16_t>(buffer[4]) << 8);
4067 retVal += sizeOfValue;
4068 }
4069 break;
4070
4074 {
4075 const std::uint32_t sizeOfMacros = (buffer[7] * 2);
4076 retVal += sizeOfMacros;
4077 }
4078 break;
4079
4081 {
4082 const std::uint32_t sizeOfValidationString = buffer[4];
4083 const std::uint32_t sizeOfMacros = (buffer[5 + sizeOfValidationString] * 2);
4084 retVal += (sizeOfMacros + sizeOfValidationString);
4085 }
4086 break;
4087
4089 {
4090 const std::uint32_t numberOfCodePlanes = buffer[5];
4091 retVal += (numberOfCodePlanes * 2); // Doesn't include the character ranges, need to handle those externally
4092 }
4093 break;
4094
4096 {
4097 const std::uint32_t numberOfMacroBytes = (static_cast<std::uint16_t>(buffer[3]) | (static_cast<std::uint16_t>(buffer[4]) << 8));
4098 retVal += numberOfMacroBytes;
4099 }
4100 break;
4101
4103 {
4104 const std::uint32_t numberIndexes = (static_cast<std::uint16_t>(buffer[3]) | (static_cast<std::uint16_t>(buffer[4]) << 8));
4105 retVal += numberIndexes;
4106 }
4107 break;
4108
4110 {
4111 const std::uint32_t sizeOfReferences = (buffer[14] * 2);
4112 const std::uint32_t numberObjects = (buffer[15] * 6);
4113 const std::uint32_t sizeOfMacros = (buffer[16] * 2);
4114 retVal += (sizeOfMacros + numberObjects + sizeOfReferences);
4115 }
4116 break;
4117
4119 {
4120 const std::uint32_t numberObjects = (buffer[8] * 2);
4121 const std::uint32_t sizeOfMacros = (buffer[9] * 2);
4122 retVal += (sizeOfMacros + numberObjects);
4123 }
4124 break;
4125
4127 {
4128 const std::uint32_t sizeOfLabeledObjects = ((static_cast<uint16_t>(buffer[4]) | static_cast<uint16_t>(buffer[5]) << 8) * 7);
4129 retVal += sizeOfLabeledObjects;
4130 }
4131 break;
4132
4134 {
4135 const std::uint32_t sizeOfObjects = (buffer[12] * 2);
4136 retVal += sizeOfObjects;
4137 }
4138 break;
4139
4141 {
4142 const std::uint32_t sizeOfObjects = (buffer[15] * 6);
4143 const std::uint32_t sizeOfMacros = (buffer[16] * 2);
4144 retVal += (sizeOfMacros + sizeOfObjects);
4145 }
4146 break;
4147
4150 {
4151 const std::uint32_t sizeOfObjects = (buffer[5] * 6);
4152 retVal += sizeOfObjects;
4153 }
4154 break;
4155
4158 {
4159 const std::uint32_t sizeOfObjects = (buffer[6] * 6);
4160 retVal += sizeOfObjects;
4161 }
4162 break;
4163
4164 default:
4165 {
4166 LOG_ERROR("[VT]: Cannot autoscale object pool due to unknown object total length - type " + isobus::to_string(static_cast<int>(buffer[2])));
4167 }
4168 break;
4169 }
4170 return retVal;
4171 }
4172
4173 void VirtualTerminalClient::process_standard_object_height_and_width(std::uint8_t *buffer, float scaleFactor)
4174 {
4175 auto width = static_cast<std::uint16_t>(((static_cast<std::uint16_t>(buffer[3]) | (static_cast<std::uint16_t>(buffer[4]) << 8))) * scaleFactor);
4176 auto height = static_cast<std::uint16_t>(((static_cast<std::uint16_t>(buffer[5]) | (static_cast<std::uint16_t>(buffer[6]) << 8))) * scaleFactor);
4177 buffer[3] = (width & 0xFF);
4178 buffer[4] = (width >> 8);
4179 buffer[5] = (height & 0xFF);
4180 buffer[6] = (height >> 8);
4181 }
4182
4183 bool VirtualTerminalClient::resize_object(std::uint8_t *buffer, float scaleFactor, VirtualTerminalObjectType type)
4184 {
4185 bool retVal = false;
4186
4187 if (get_is_object_scalable(type))
4188 {
4189 switch (type)
4190 {
4192 {
4193 const std::uint8_t childrenToFollow = buffer[6];
4194
4195 for (std::uint_fast8_t i = 0; i < childrenToFollow; i++)
4196 {
4197 auto childX = static_cast<std::int16_t>(((static_cast<std::int16_t>(buffer[10 + (6 * i)]) | (static_cast<std::int16_t>(buffer[11 + (6 * i)]) << 8))) * scaleFactor);
4198 auto childY = static_cast<std::int16_t>(((static_cast<std::int16_t>(buffer[12 + (6 * i)]) | (static_cast<std::int16_t>(buffer[13 + (6 * i)]) << 8))) * scaleFactor);
4199 buffer[10 + (6 * i)] = (childX & 0xFF);
4200 buffer[11 + (6 * i)] = (childX >> 8);
4201 buffer[12 + (6 * i)] = (childY & 0xFF);
4202 buffer[13 + (6 * i)] = (childY >> 8);
4203 }
4204 retVal = true;
4205 }
4206 break;
4207
4209 {
4210 const std::uint8_t childrenToFollow = buffer[8];
4211
4212 for (std::uint_fast8_t i = 0; i < childrenToFollow; i++)
4213 {
4214 auto childX = static_cast<std::int16_t>(((static_cast<std::int16_t>(buffer[12 + (6 * i)]) | (static_cast<std::int16_t>(buffer[13 + (6 * i)]) << 8))) * scaleFactor);
4215 auto childY = static_cast<std::int16_t>(((static_cast<std::int16_t>(buffer[14 + (6 * i)]) | (static_cast<std::int16_t>(buffer[15 + (6 * i)]) << 8))) * scaleFactor);
4216 buffer[12 + (6 * i)] = (childX & 0xFF);
4217 buffer[13 + (6 * i)] = (childX >> 8);
4218 buffer[14 + (6 * i)] = (childY & 0xFF);
4219 buffer[15 + (6 * i)] = (childY >> 8);
4220 }
4221 retVal = true;
4222 }
4223 break;
4224
4226 {
4227 std::uint8_t childrenToFollow = buffer[8];
4228
4229 // Modify the object in memory
4230 process_standard_object_height_and_width(buffer, scaleFactor);
4231
4232 // Iterate over the list of children and move them proportionally to the new size
4233 // The container is 10 bytes, followed by children with 2 bytes of ID, 2 of X, and 2 of Y
4234 for (std::uint_fast8_t i = 0; i < childrenToFollow; i++)
4235 {
4236 auto childX = static_cast<std::int16_t>(((static_cast<std::int16_t>(buffer[12 + (6 * i)]) | (static_cast<std::int16_t>(buffer[13 + (6 * i)]) << 8))) * scaleFactor);
4237 auto childY = static_cast<std::int16_t>(((static_cast<std::int16_t>(buffer[14 + (6 * i)]) | (static_cast<std::int16_t>(buffer[15 + (6 * i)]) << 8))) * scaleFactor);
4238 buffer[12 + (6 * i)] = (childX & 0xFF);
4239 buffer[13 + (6 * i)] = (childX >> 8);
4240 buffer[14 + (6 * i)] = (childY & 0xFF);
4241 buffer[15 + (6 * i)] = (childY >> 8);
4242 }
4243 retVal = true;
4244 }
4245 break;
4246
4248 {
4249 std::uint8_t childrenToFollow = buffer[11];
4250
4251 // Modify the object in memory
4252 process_standard_object_height_and_width(buffer, scaleFactor);
4253
4254 // Iterate over the list of children and move them proportionally to the new size
4255 for (std::uint_fast8_t i = 0; i < childrenToFollow; i++)
4256 {
4257 auto childWidth = static_cast<std::uint16_t>(((static_cast<std::uint16_t>(buffer[15 + (6 * i)]) | (static_cast<std::uint16_t>(buffer[16 + (6 * i)]) << 8))) * scaleFactor);
4258 auto childHeight = static_cast<std::uint16_t>(((static_cast<std::uint16_t>(buffer[17 + (6 * i)]) | (static_cast<std::uint16_t>(buffer[18 + (6 * i)]) << 8))) * scaleFactor);
4259 buffer[15 + (6 * i)] = (childWidth & 0xFF);
4260 buffer[16 + (6 * i)] = (childWidth >> 8);
4261 buffer[17 + (6 * i)] = (childHeight & 0xFF);
4262 buffer[18 + (6 * i)] = (childHeight >> 8);
4263 }
4264 retVal = true;
4265 }
4266 break;
4267
4269 {
4270 auto width = static_cast<std::uint16_t>((static_cast<std::uint16_t>(buffer[4]) | (static_cast<std::uint16_t>(buffer[5]) << 8)));
4271
4272 // Modify the object in memory
4273 buffer[4] = (width & 0xFF);
4274 buffer[5] = (width >> 8);
4275 retVal = true;
4276 }
4277 break;
4278
4286 {
4287 // Modify the object in memory
4288 process_standard_object_height_and_width(buffer, scaleFactor);
4289 retVal = true;
4290 }
4291 break;
4292
4296 {
4297 // Modify the object in memory
4298 auto width = static_cast<std::uint16_t>(((static_cast<std::uint16_t>(buffer[5]) | (static_cast<std::uint16_t>(buffer[6]) << 8))) * scaleFactor);
4299 auto height = static_cast<std::uint16_t>(((static_cast<std::uint16_t>(buffer[7]) | (static_cast<std::uint16_t>(buffer[8]) << 8))) * scaleFactor);
4300 buffer[5] = (width & 0xFF);
4301 buffer[6] = (width >> 8);
4302 buffer[7] = (height & 0xFF);
4303 buffer[8] = (height >> 8);
4304 retVal = true;
4305 }
4306 break;
4307
4309 {
4310 const std::uint8_t numberOfPoints = buffer[12];
4311
4312 // Modify the object in memory
4313 process_standard_object_height_and_width(buffer, scaleFactor);
4314
4315 // Reposition the child points
4316 for (std::uint_fast8_t i = 0; i < numberOfPoints; i++)
4317 {
4318 auto xPosition = static_cast<std::uint16_t>(((static_cast<std::uint16_t>(buffer[14 + (4 * i)]) | (static_cast<std::uint16_t>(buffer[15 + (4 * i)]) << 8))) * scaleFactor);
4319 auto yPosition = static_cast<std::uint16_t>(((static_cast<std::uint16_t>(buffer[16 + (4 * i)]) | (static_cast<std::uint16_t>(buffer[17 + (4 * i)]) << 8))) * scaleFactor);
4320 buffer[14 + (4 * i)] = (xPosition & 0xFF);
4321 buffer[15 + (4 * i)] = (xPosition >> 8);
4322 buffer[16 + (4 * i)] = (yPosition & 0xFF);
4323 buffer[17 + (4 * i)] = (yPosition >> 8);
4324 }
4325 retVal = true;
4326 }
4327 break;
4328
4331 {
4332 // Modify the object in memory
4333 auto width = static_cast<std::uint16_t>(((static_cast<std::uint16_t>(buffer[3]) | (static_cast<std::uint16_t>(buffer[4]) << 8))) * scaleFactor);
4334 buffer[3] = (width & 0xFF);
4335 buffer[4] = (width >> 8);
4336 retVal = true;
4337 }
4338 break;
4339
4341 {
4342 // Modify the object in memory
4343 process_standard_object_height_and_width(buffer, scaleFactor);
4344
4345 auto width = static_cast<std::uint16_t>(((static_cast<std::uint16_t>(buffer[12]) | (static_cast<std::uint16_t>(buffer[13]) << 8))) * scaleFactor);
4346 buffer[12] = (width & 0xFF);
4347 buffer[13] = (width >> 8);
4348 retVal = true;
4349 }
4350 break;
4351
4353 {
4354 std::uint8_t childrenToFollow = buffer[15];
4355
4356 // Modify the object in memory
4357 process_standard_object_height_and_width(buffer, scaleFactor);
4358
4359 // Iterate over the list of children and move them proportionally to the new size
4360 for (std::uint_fast8_t i = 0; i < childrenToFollow; i++)
4361 {
4362 auto childX = static_cast<std::int16_t>(((static_cast<std::int16_t>(buffer[20 + (6 * i)]) | (static_cast<std::int16_t>(buffer[21 + (6 * i)]) << 8))) * scaleFactor);
4363 auto childY = static_cast<std::int16_t>(((static_cast<std::int16_t>(buffer[22 + (6 * i)]) | (static_cast<std::int16_t>(buffer[23 + (6 * i)]) << 8))) * scaleFactor);
4364 buffer[20 + (6 * i)] = (childX & 0xFF);
4365 buffer[21 + (6 * i)] = (childX >> 8);
4366 buffer[22 + (6 * i)] = (childY & 0xFF);
4367 buffer[23 + (6 * i)] = (childY >> 8);
4368 }
4369 retVal = true;
4370 }
4371 break;
4372
4374 {
4375 const std::uint8_t childrenToFollow = buffer[5];
4376
4377 for (std::uint_fast8_t i = 0; i < childrenToFollow; i++)
4378 {
4379 auto childX = static_cast<std::int16_t>(((static_cast<std::int16_t>(buffer[9 + (6 * i)]) | (static_cast<std::int16_t>(buffer[10 + (6 * i)]) << 8))) * scaleFactor);
4380 auto childY = static_cast<std::int16_t>(((static_cast<std::int16_t>(buffer[11 + (6 * i)]) | (static_cast<std::int16_t>(buffer[12 + (6 * i)]) << 8))) * scaleFactor);
4381 buffer[9 + (6 * i)] = (childX & 0xFF);
4382 buffer[10 + (6 * i)] = (childX >> 8);
4383 buffer[11 + (6 * i)] = (childY & 0xFF);
4384 buffer[12 + (6 * i)] = (childY >> 8);
4385 }
4386 retVal = true;
4387 }
4388 break;
4389
4391 {
4392 buffer[4] = static_cast<std::uint8_t>(get_font_or_next_smallest_font(remap_font_to_scale(static_cast<FontSize>(buffer[4]), scaleFactor)));
4393 retVal = true;
4394 }
4395 break;
4396
4400 {
4401 std::uint8_t childrenToFollow = buffer[5];
4402
4403 for (std::uint_fast8_t i = 0; i < childrenToFollow; i++)
4404 {
4405 auto childX = static_cast<std::int16_t>(((static_cast<std::int16_t>(buffer[8 + (6 * i)]) | (static_cast<std::int16_t>(buffer[9 + (6 * i)]) << 8))) * scaleFactor);
4406 auto childY = static_cast<std::int16_t>(((static_cast<std::int16_t>(buffer[10 + (6 * i)]) | (static_cast<std::int16_t>(buffer[11 + (6 * i)]) << 8))) * scaleFactor);
4407 buffer[8 + (6 * i)] = (childX & 0xFF);
4408 buffer[9 + (6 * i)] = (childX >> 8);
4409 buffer[10 + (6 * i)] = (childY & 0xFF);
4410 buffer[11 + (6 * i)] = (childY >> 8);
4411 }
4412 retVal = true;
4413 }
4414 break;
4415
4417 {
4418 std::uint8_t childrenToFollow = buffer[6];
4419
4420 for (std::uint_fast8_t i = 0; i < childrenToFollow; i++)
4421 {
4422 auto childX = static_cast<std::int16_t>(((static_cast<std::int16_t>(buffer[9 + (6 * i)]) | (static_cast<std::int16_t>(buffer[10 + (6 * i)]) << 8))) * scaleFactor);
4423 auto childY = static_cast<std::int16_t>(((static_cast<std::int16_t>(buffer[11 + (6 * i)]) | (static_cast<std::int16_t>(buffer[12 + (6 * i)]) << 8))) * scaleFactor);
4424 buffer[9 + (6 * i)] = (childX & 0xFF);
4425 buffer[10 + (6 * i)] = (childX >> 8);
4426 buffer[11 + (6 * i)] = (childY & 0xFF);
4427 buffer[12 + (6 * i)] = (childY >> 8);
4428 }
4429 retVal = true;
4430 }
4431 break;
4432
4433 default:
4434 {
4435 LOG_DEBUG("[VT]: Skipping resize of non-resizable object type " +
4436 isobus::to_string(static_cast<int>(type)));
4437 retVal = false;
4438 }
4439 break;
4440 }
4441 }
4442 else
4443 {
4444 LOG_DEBUG("[VT]: Skipping resize of non-resizable object type " +
4445 isobus::to_string(static_cast<int>(type)));
4446 retVal = true;
4447 }
4448 return retVal;
4449 }
4450
4451 bool VirtualTerminalClient::is_function_unsupported(std::uint8_t functionCode) const
4452 {
4453 return std::find(unsupportedFunctions.begin(), unsupportedFunctions.end(), functionCode) != unsupportedFunctions.end();
4454 }
4455
4457 {
4458 return is_function_unsupported(static_cast<std::uint8_t>(function));
4459 }
4460
4461 bool VirtualTerminalClient::send_command(const std::vector<std::uint8_t> &data)
4462 {
4464 {
4465 if (SystemTiming::time_expired_ms(lastCommandTimestamp_ms, 1500))
4466 {
4467 LOG_WARNING("[VT]: Server response to a command timed out");
4469 }
4470 else
4471 {
4472 // We're still waiting for a response to the last command, so we can't send another one yet
4473 return false;
4474 }
4475 }
4476
4477 if (!get_is_connected())
4478 {
4479 LOG_ERROR("[VT]: Cannot send command, not connected");
4480 return false;
4481 }
4482
4483 bool success = send_message_to_vt(data.data(), data.size());
4484
4485 if (success)
4486 {
4488 lastCommandTimestamp_ms = SystemTiming::get_timestamp_ms();
4489 }
4490 return success;
4491 }
4492
4493 bool VirtualTerminalClient::queue_command(const std::vector<std::uint8_t> &data, bool replace)
4494 {
4495 std::uint8_t functionCode = data[0];
4496 if (is_function_unsupported(functionCode))
4497 {
4498 return false;
4499 }
4500
4501 if (get_is_connected() && send_command(data))
4502 {
4503 return true;
4504 }
4505
4506 LOCK_GUARD(Mutex, commandQueueMutex);
4507 if (replace && replace_command(data))
4508 {
4509 return true;
4510 }
4511 commandQueue.emplace_back(data);
4512 return true;
4513 }
4514
4515 bool VirtualTerminalClient::replace_command(const std::vector<std::uint8_t> &data)
4516 {
4517 bool alreadyReplaced = false;
4518 for (auto it = commandQueue.begin(); it != commandQueue.end();)
4519 {
4520 bool matchesFunctionCode = (it->at(0) == data[0]);
4521 if (matchesFunctionCode)
4522 {
4523 if (!alreadyReplaced)
4524 {
4525 *it = data;
4526 alreadyReplaced = true;
4527 it++;
4528 }
4529 else
4530 {
4531 it = commandQueue.erase(it);
4532 }
4533 }
4534 else
4535 {
4536 it++;
4537 }
4538 }
4539 return alreadyReplaced;
4540 }
4541
4543 {
4544 if (!get_is_connected())
4545 {
4546 return;
4547 }
4548 LOCK_GUARD(Mutex, commandQueueMutex);
4549 for (auto it = commandQueue.begin(); it != commandQueue.end();)
4550 {
4551 if (send_command(*it))
4552 {
4553 it = commandQueue.erase(it);
4554 }
4555 else
4556 {
4557 it++;
4558 }
4559 }
4560 }
4561
4563 {
4564#if !defined CAN_STACK_DISABLE_THREADS && !defined ARDUINO
4565 for (;;)
4566 {
4567 if (shouldTerminate)
4568 {
4569 break;
4570 }
4571 update();
4572 std::this_thread::sleep_for(std::chrono::milliseconds(50));
4573 }
4574#endif
4575 }
4576
4577} // 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...
CANPriority
Defines all the CAN frame priorities that can be encoded in a frame ID.
@ Priority3
Priority highest - 3 (Control messages priority)
@ Priority5
Priority highest - 5.
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.
const std::vector< std::uint8_t > & get_data() const
Gets a reference to the data in the CAN message.
std::uint32_t get_data_length() const
Returns the length of the data in the CAN message.
std::uint32_t get_uint24_at(const std::uint32_t index, const ByteFormat format=ByteFormat::LittleEndian) const
Get a right-aligned 24-bit integer from the buffer (returned as a uint32_t) at a specific index....
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...
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...
CANIdentifier get_identifier() const
Returns the identifier of the message.
bool get_bool_at(const std::uint32_t byteIndex, const std::uint8_t bitIndex, const std::uint8_t length=1) const
Get a bit-boolean from the buffer at a specific index.
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.
std::uint64_t get_uint64_at(const std::uint32_t index, const ByteFormat format=ByteFormat::LittleEndian) const
Get a 64-bit unsigned integer from the buffer at a specific index. A 64-bit unsigned integer can hold...
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 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)
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.
bool get_initialized() const
Returns if initialize has been called yet.
A struct for storing information of a function assigned to an auxiliary input.
std::uint16_t inputObjectID
The object ID assigned on the auxiliary inputs end.
AssignedAuxiliaryFunction(std::uint16_t functionObjectID, std::uint16_t inputObjectID, AuxiliaryTypeTwoFunctionType functionType)
Constructs a AssignedAuxiliaryFunction, sets default values.
bool operator==(const AssignedAuxiliaryFunction &other) const
Allows easy comparison of two AssignedAuxiliaryFunction objects.
AuxiliaryTypeTwoFunctionType functionType
The type of function.
std::uint16_t functionObjectID
The object ID of the function present in our object pool.
An client interface for interacting with a virtual terminal (VT) server.
bool send_change_font_attributes(std::uint16_t objectID, std::uint8_t colour, FontSize size, std::uint8_t type, std::uint8_t styleBitfield)
Sends the change font attributes command.
bool send_extended_load_version(std::array< std::uint8_t, 32 > versionLabel) const
Sends the extended load version message.
HideShowObjectCommand
Enumerates the states that can be sent with a hide/show object command.
EventDispatcher< VTUserLayoutHideShowEvent > & get_vt_user_layout_hide_show_event_dispatcher()
The event dispatcher for when a user-layout object is hidden or shown.
bool send_set_graphics_cursor(std::uint16_t objectID, std::int16_t xPosition, std::int16_t yPosition)
Sends the set graphics cursor command.
EventDispatcher< VTChangeActiveMaskEvent > changeActiveMaskEventDispatcher
A list of all change active mask callbacks.
bool get_support_pointing_device_with_pointing_message() const
Returns if the VT server supports a pointing device with pointing message.
bool send_pan_and_zoom_viewport(std::uint16_t objectID, std::int16_t xAttribute, std::int16_t yAttribute, float zoom)
Sends the pan and zoom viewport command.
bool send_change_polygon_scale(std::uint16_t objectID, std::uint16_t widthAttribute, std::uint16_t heightAttribute)
Sends the change polygon scale command.
bool get_support_drag_operation() const
Returns if the VT supports the drag operation.
void process_command_queue()
Tries to send all messages in the queue.
bool send_auxiliary_input_maintenance() const
Send the auxiliary control type 2 maintenance message.
static void process_standard_object_height_and_width(std::uint8_t *buffer, float scaleFactor)
Resizes the most common VT object format by some scale factor.
@ SetLineAttributesObjectID
Sets the line attribute object ID.
@ MoveGraphicsCursor
Moves the cursor relative to current location.
@ SetGraphicsCursor
Sets the graphics cursor x/y attributes.
@ CopyViewportToPictureGraphic
Copies the viewport to picture graphic object.
@ CopyCanvasToPictureGraphic
Copies the canvas to picture graphic object.
@ SetFontAttributesObjectID
Sets the font attribute object ID.
@ SetFillAttributesObjectID
Sets the fill attribute object ID.
std::uint8_t get_active_working_set_master_address() const
Returns the active working set master's address.
bool commandAwaitingResponse
Determines if we are currently waiting for a response to a command.
bool get_support_intermediate_coordinates_during_drag_operations() const
Returns if the VT supports the intermediate coordinates during a drag operation.
bool send_set_font_attributes_object_id(std::uint16_t objectID, std::uint16_t fontAttributesObjectID)
Sends the set fill attributes object ID command.
std::uint8_t connectedVTVersion
The VT server's supported max version.
CurrentObjectPoolUploadState currentObjectPoolState
The current upload state of the object pool being processed.
std::shared_ptr< PartneredControlFunction > get_partner_control_function() const
Returns the control function of the VT server with which this VT client communicates.
void initialize(bool spawnThread)
This function starts the state machine. Call this once you have supplied 1 or more object pool and ar...
std::vector< ObjectPoolDataStruct > objectPools
A container to hold all object pools that have been assigned to the interface.
EventDispatcher< AuxiliaryFunctionEvent > & get_auxiliary_function_event_dispatcher()
The event dispatcher for for when a change in auxiliary input for a function is received.
EventDispatcher< VTChangeStringValueEvent > changeStringValueEventDispatcher
A list of all change string value callbacks.
bool send_store_version(std::array< std::uint8_t, 7 > versionLabel) const
Sends the store version message.
bool send_set_foreground_colour(std::uint16_t objectID, std::uint8_t colour)
Sends the set foreground colour command.
std::uint8_t currentCommandFunctionCode
The VT server's current command function code.
std::uint16_t yPixels
The y pixel dimension as reported by the VT server.
EventDispatcher< VTKeyEvent > softKeyEventDispatcher
A list of all soft key event callbacks.
LanguageCommandInterface languageCommandInterface
Used to determine the language and unit systems in use by the VT server.
bool send_change_child_position(std::uint16_t objectID, std::uint16_t parentObjectID, std::uint16_t xPosition, std::uint16_t yPosition)
Sends the change child position command.
EventDispatcher< VTESCMessageEvent > & get_vt_esc_message_event_dispatcher()
The event dispatcher for when an ESC message is received, e.g. an open object input is closed.
bool send_change_polygon_point(std::uint16_t objectID, std::uint8_t pointIndex, std::uint16_t newXValue, std::uint16_t newYValue)
Sends change polygon point command.
bool replace_command(const std::vector< std::uint8_t > &data)
Replaces the first message in the queue with the same function-code and priority, and removes the res...
Mutex commandQueueMutex
A mutex to protect the command queue.
void set_auxiliary_input_model_identification_code(std::uint16_t modelIdentificationCode)
Set the model identification code of our auxiliary input device.
bool send_select_active_working_set(std::uint64_t NAMEofWorkingSetMasterForDesiredWorkingSet)
Sends the select active working set command.
bool send_get_supported_widechars() const
Sends the get supported widechars message.
EventDispatcher< VTPointingEvent > pointingEventDispatcher
A list of all pointing event callbacks.
bool send_draw_point(std::uint16_t objectID, std::int16_t xOffset, std::int16_t yOffset)
Sends the draw point command.
static std::uint32_t get_minimum_object_length(VirtualTerminalObjectType type)
Returns the minimum length that the specified object could possibly require in bytes.
EventDispatcher< VTChangeNumericValueEvent > changeNumericValueEventDispatcher
A list of all change numeric value callbacks.
bool send_get_supported_objects() const
Sends the get supported objects message.
bool send_enable_disable_object(std::uint16_t objectID, EnableDisableObjectCommand command)
Sends an enable/disable object command.
void worker_thread_function()
The worker thread will execute this function when it runs, if applicable.
void set_object_pool(std::uint8_t poolIndex, const std::uint8_t *pool, std::uint32_t size, std::string version="")
Assigns an object pool to the client using a buffer and size.
GraphicMode supportedGraphicsMode
The graphics mode reported by the VT server.
StateMachineState
The internal state machine state of the VT client, mostly just public so tests can access it.
@ SendLoadVersion
Sending the load version command.
@ WaitForEndOfObjectPoolResponse
Client is waiting for the end of object pool response message.
@ WaitForGetNumberSoftKeysResponse
Client is waiting for a response to the "get number of soft keys" message.
@ WaitForGetVersionsResponse
Client is waiting for a response to the "get versions" message.
@ SendEndOfObjectPool
Client is sending the end of object pool message.
@ Connected
Client is connected to the VT server and the application layer is in control.
@ SendGetVersions
If a version label was specified, check to see if the VT has that version already.
@ SendGetTextFontData
Client is sending the "get text font data" message.
@ SendGetMemory
Client is sending the "get memory" message to see if VT has enough memory available.
@ SendGetHardware
Client is sending the "get hardware" message.
@ SendWorkingSetMasterMessage
Client is sending the working state master message.
@ WaitForGetMemoryResponse
Client is waiting for a response to the "get memory" message.
@ UploadObjectPool
Client is uploading the object pool.
@ SendGetNumberSoftkeys
Client is sending the "get number of soft keys" message.
@ WaitForStoreVersionResponse
Client is waiting for a response to the store version command.
@ ReadyForObjectPool
Client needs an object pool before connection can continue.
@ WaitForGetHardwareResponse
Client is waiting for a response to the "get hardware" message.
@ WaitForLoadVersionResponse
Client is waiting for the VT to respond to the "Load Version" command.
@ SendStoreVersion
Sending the store version command.
@ WaitForGetTextFontDataResponse
Client is waiting for a response to the "get text font data" message.
@ Failed
Client could not connect to the VT due to an error.
@ Disconnected
VT is not connected, and is not trying to connect yet.
@ WaitForPartnerVTStatusMessage
VT client is initialized, waiting for a VT server to come online.
std::uint32_t lastVTStatusTimestamp_ms
The timestamp of the last VT status message.
std::uint8_t smallFontSizesBitfield
The small font sizes supported by the VT server.
bool send_select_colour_map_or_palette(std::uint16_t objectID)
Sends the select colour map or palette command.
std::uint16_t get_visible_soft_key_mask() const
Returns the current soft key mask displayed by the VT server.
std::uint16_t get_number_x_pixels() const
Returns the number of x pixels in the data mask area.
bool send_change_object_label(std::uint16_t objectID, std::uint16_t labelStringObjectID, std::uint8_t fontType, std::uint16_t graphicalDesignatorObjectID)
Sends the change object label command.
bool send_extended_get_versions() const
Sends the get extended versions message.
bool get_support_touchscreen_with_pointing_message() const
Returns if the VT server supports a touchscreen with pointing message.
std::uint8_t softKeyYAxisPixels
The size of a soft key Y dimension as reported by the VT server.
static void process_flags(std::uint32_t flag, void *parent)
Processes the internal Tx flags.
bool send_change_list_item(std::uint16_t objectID, std::uint8_t listIndex, std::uint16_t newObjectID)
Sends the change list item command.
AlarmMaskPriority
The allowable priorities of an alarm mask.
EventDispatcher< VTKeyEvent > buttonEventDispatcher
A list of all button event callbacks.
bool send_zoom_viewport(std::uint16_t objectID, float zoom)
Sends the zoom viewport command.
bool is_function_unsupported(Function function) const
Extract from the cache whether a VT does not support a specific function.
bool send_set_audio_volume(std::uint8_t volume_percent)
Sends the set audio volume command.
void update_auxiliary_input(const std::uint16_t auxiliaryInputID, const std::uint16_t value1, const std::uint16_t value2, const bool controlLocked=false)
Update the state of an auxiliary input. This should be called when the value of an auxiliary input ch...
bool send_change_string_value(std::uint16_t objectID, uint16_t stringLength, const char *value)
Sends the change string value command.
EventDispatcher< VTESCMessageEvent > escMessageEventDispatcher
A list of all ESC event callbacks.
bool send_execute_extended_macro(std::uint16_t objectID)
Sends the execute extended macro command.
std::uint16_t get_number_y_pixels() const
Returns the number of y pixels in the data mask area.
VirtualTerminalClient(std::shared_ptr< PartneredControlFunction > partner, std::shared_ptr< InternalControlFunction > clientSource)
The constructor for a VirtualTerminalClient.
std::uint8_t activeWorkingSetMasterAddress
The active working set master address.
LineDirection
Enumerates the different line directions that can be used when changing an endpoint of an object.
bool get_auxiliary_input_learn_mode_enabled() const
Get whether the VT has enabled the learn mode for the auxiliary input.
bool is_vt_version_supported(VTVersion value) const
Returns whether the VT version is supported by the VT server.
FontStyleBits
Enumerates the font style options that can be encoded in a font style bitfield.
std::uint16_t xPixels
The x pixel dimension as reported by the VT server.
std::uint8_t get_number_virtual_softkeys() const
Returns the number of virtual softkeys reported by the VT server.
KeyActivationCode
The different key activation codes that a button press can generate.
@ ButtonPressAborted
Press was aborted (user navigated away from the button and did not release it)
std::uint16_t activeWorkingSetDataMaskObjectID
The active working set data mask object ID.
bool send_copy_viewport_to_picture_graphic(std::uint16_t graphicsContextObjectID, std::uint16_t objectID)
Sends the copy viewport to picture graphic command.
bool firstTimeInState
Stores if the current update cycle is the first time a state machine state has been processed.
static FontSize remap_font_to_scale(FontSize originalFont, float scaleFactor)
Remaps a font to some other font based on a scale factor This is not a one-size-fits-all solution,...
GraphicMode
Enumerates the various VT server graphics modes.
@ TwoHundredFiftySixColour
256 Colour mode (8 bit)
std::uint16_t activeWorkingSetSoftKeyMaskObjectID
The active working set's softkey mask object ID.
bool get_support_simultaneous_activation_buttons_and_softkeys() const
Returns if the VT server supports simultaneous activation of buttons and softkeys.
bool send_change_fill_attributes(std::uint16_t objectID, FillType fillType, std::uint8_t colour, std::uint16_t fillPatternObjectID)
Sends the change fill attributes command.
std::uint8_t softKeyXAxisPixels
The size of a soft key X dimension as reported by the VT server.
void set_object_pool_scaling(std::uint8_t poolIndex, std::uint32_t originalDataMaskDimensions_px, std::uint32_t originalSoftKyeDesignatorHeight_px)
Configures an object pool to be automatically scaled to match the target VT server.
bool send_get_memory(std::uint32_t requiredMemory) const
Sends the get memory message.
std::vector< std::vector< std::uint8_t > > commandQueue
A queue of commands to send to the VT server.
static bool process_internal_object_pool_upload_callback(std::uint32_t callbackIndex, std::uint32_t bytesOffset, std::uint32_t numberOfBytesNeeded, std::uint8_t *chunkBuffer, void *parentPointer)
The data callback passed to the network manger's send function for the transport layer messages.
bool send_hide_show_object(std::uint16_t objectID, HideShowObjectCommand command)
Sends a hide/show object command.
bool send_pan_viewport(std::uint16_t objectID, std::int16_t xAttribute, std::int16_t yAttribute)
Sends the pan viewport command.
bool send_change_size_command(std::uint16_t objectID, std::uint16_t newWidth, std::uint16_t newHeight)
Sends the change size command.
std::uint16_t ourModelIdentificationCode
The model identification code of this input device.
bool send_change_softkey_mask(MaskType type, std::uint16_t dataOrAlarmMaskObjectID, std::uint16_t newSoftKeyMaskObjectID)
Sends the change softkey mask command.
std::uint32_t lastCommandTimestamp_ms
The timestamp of the last command sent.
void add_auxiliary_input_object_id(const std::uint16_t auxiliaryInputID)
Add a new auxiliary input to be managed by this virtual terminal object.
bool send_change_viewport_size(std::uint16_t objectID, std::uint16_t width, std::uint16_t height)
Sends the change viewport size command.
bool get_is_connected() const
Check whether the client is connected to the VT server.
std::uint16_t get_visible_data_mask() const
Returns the current data mask displayed by the VT server.
EventDispatcher< VTKeyEvent > & get_vt_soft_key_event_dispatcher()
The event dispatcher for when a soft key is pressed or released.
@ InProgress
The object pool upload is in progress.
@ Uninitialized
The object pool upload has not been started.
static void process_callback(std::uint32_t parameterGroupNumber, std::uint32_t dataLength, std::shared_ptr< InternalControlFunction > sourceControlFunction, std::shared_ptr< ControlFunction > destinationControlFunction, bool successful, void *parentPointer)
The callback passed to the network manager's send function to know when a Tx is completed.
bool send_change_priority(std::uint16_t alarmMaskObjectID, AlarmMaskPriority priority)
Sends the change priority command.
EventDispatcher< VTAudioSignalTerminationEvent > audioSignalTerminationEventDispatcher
A list of all control audio signal termination callbacks.
FontSize
Enumerates the different font sizes.
bool get_is_initialized() const
Returns if the client has been initialized.
bool send_lock_unlock_mask(MaskLockState state, std::uint16_t objectID, std::uint16_t timeout_ms)
Sends the lock unlock mask command.
EventDispatcher< VTChangeSoftKeyMaskEvent > changeSoftKeyMaskEventDispatcher
A list of all change soft key mask callbacks.
static constexpr std::uint32_t AUXILIARY_MAINTENANCE_TIMEOUT_MS
The delay between auxiliary maintenance messages.
VTVersion get_connected_vt_version() const
Returns the VT version supported supported by the VT server.
static std::uint32_t get_number_bytes_in_object(std::uint8_t *buffer)
Returns the total number of bytes in the VT object located at the specified memory location.
EventDispatcher< VTUserLayoutHideShowEvent > userLayoutHideShowEventDispatcher
A list of all user layout hide/show callbacks.
std::map< std::uint16_t, AuxiliaryInputState > ourAuxiliaryInputs
The inputs on this auxiliary input device.
bool send_auxiliary_functions_preferred_assignment() const
Send the preferred auxiliary control type 2 assignment command.
bool send_load_version(std::array< std::uint8_t, 7 > versionLabel) const
Sends the load version message.
void update()
Periodic Update Function (worker thread may call this)
bool send_execute_macro(std::uint16_t objectID)
Sends the execute macro command.
static bool get_is_object_scalable(VirtualTerminalObjectType type)
Returns if the specified object type can be scaled.
EventDispatcher< VTChangeStringValueEvent > & get_vt_change_string_value_event_dispatcher()
The event dispatcher for when a string value is changed.
EventDispatcher< VTPointingEvent > & get_vt_pointing_event_dispatcher()
The event dispatcher for when a pointing event is "pressed or released".
std::shared_ptr< InternalControlFunction > myControlFunction
The internal control function the client uses to send from.
void remove_auxiliary_input_object_id(const std::uint16_t auxiliaryInputID)
Remove an auxiliary input from the pool of managed auxiliary inputs.
bool resize_object(std::uint8_t *buffer, float scaleFactor, VirtualTerminalObjectType type)
Resizes a single VT object by some scale factor.
std::uint32_t lastWorkingSetMaintenanceTimestamp_ms
The timestamp from the last time we sent the maintenance message.
std::uint8_t busyCodesBitfield
The VT server's busy codes.
TransmitFlags
Flags used as a retry mechanism for sending important messages.
@ SendWorkingSetMaintenance
Flag to send the working set maintenenace message.
@ NumberFlags
The number of flags in this enum.
@ SendAuxiliaryMaintenance
Flag to send the auxiliary maintenance message.
bool send_draw_closed_ellipse(std::uint16_t objectID, std::uint16_t width, std::uint16_t height)
Sends the draw closed ellipse message.
EventDispatcher< VTAudioSignalTerminationEvent > & get_vt_control_audio_signal_termination_event_dispatcher()
The event dispatcher for when an audio signal is terminated.
Function
Enumerates the multiplexor byte values for VT commands.
bool queue_command(const std::vector< std::uint8_t > &data, bool replace=false)
Tries to send a command to the VT server, and queues it if it fails.
bool send_get_attribute_value(std::uint16_t objectID, std::uint8_t attributeID)
Sends the get attribute value message.
bool send_end_of_object_pool() const
Sends the end of object pool message.
bool get_any_pool_needs_scaling() const
Returns if any object pool had scaling configured.
EventDispatcher< AuxiliaryFunctionEvent > auxiliaryFunctionEventDispatcher
A list of all auxiliary function callbacks.
bool get_has_adjustable_volume_output() const
Returns if the VT server supports adjustable volume output.
bool sendWorkingSetMaintenance
Used internally to enable and disable cyclic sending of the working set maintenance message.
FillType
Enumerates the different fill types for an object.
bool send_draw_polygon(std::uint16_t objectID, std::uint8_t numberOfPoints, const std::int16_t *listOfXOffsetsRelativeToCursor, const std::int16_t *listOfYOffsetsRelativeToCursor)
Sends the draw polygon command.
static constexpr std::uint64_t AUXILIARY_INPUT_STATUS_DELAY
The delay between the auxiliary input status messages, in milliseconds.
bool send_ESC()
Sends the ESC message (Escape)
void update_auxiliary_input_status()
Send the auxiliary control type 2 status message for all inputs if applicable.
bool send_draw_text(std::uint16_t objectID, bool transparent, std::uint8_t textLength, const char *value)
Sends the draw text command.
bool shouldTerminate
Used to determine if the client should exit and join the worker thread.
bool send_change_line_attributes(std::uint16_t objectID, std::uint8_t colour, std::uint8_t width, std::uint16_t lineArtBitmask)
Sends the change line attributes command.
void register_object_pool_data_chunk_callback(std::uint8_t poolIndex, std::uint32_t poolTotalSize, DataChunkCallback value, std::string version="")
Assigns an object pool to the client where the client will get data in chunks during upload.
AuxiliaryTypeTwoFunctionType
Enumerates the various auxiliary input function types.
@ QuadratureBooleanMomentary
Two Quadrature mounted Three-position switches (returns to centre position) (Momentary Single Pole,...
bool get_font_size_supported(FontSize value) const
Returns if the selected font is supported.
std::uint8_t numberVirtualSoftkeysPerSoftkeyMask
The number of virtual softkeys per softkey mask as reported by the VT server.
std::uint8_t hardwareFeaturesBitfield
The reported hardware features from the VT server.
bool send_extended_store_version(std::array< std::uint8_t, 32 > versionLabel) const
Sends the extended store version message.
ESCMessageErrorCode
Enumerates the errors that can be present in an ESC message.
@ OtherError
Error is not one of the above.
bool get_multiple_frequency_audio_output() const
Returns if the VT server supports multiple frequency audio output.
bool get_font_style_supported(FontStyleBits value) const
Returns if the selected font style is supported.
void restart_communication()
Halts communication with the VT gracefully and restarts it.
EventDispatcher< VTChangeNumericValueEvent > & get_vt_change_numeric_value_event_dispatcher()
The event dispatcher for when a numeric value is changed in an input object.
bool send_move_graphics_cursor(std::uint16_t objectID, std::int16_t xOffset, std::int16_t yOffset)
Sends the move graphics cursor command.
std::vector< std::uint8_t > unsupportedFunctions
Holds the functions unsupported by the server.
bool send_set_line_attributes_object_id(std::uint16_t objectID, std::uint16_t lineAttributeobjectID)
Sends the set line attributes object id.
void set_state(StateMachineState value)
Sets the state machine state and updates the associated timestamp.
bool send_set_background_colour(std::uint16_t objectID, std::uint8_t colour)
Sends the set background colour command.
static constexpr std::uint32_t WORKING_SET_MAINTENANCE_TIMEOUT_MS
The delay between working set maintenance messages.
bool send_change_endpoint(std::uint16_t objectID, std::uint16_t width_px, std::uint16_t height_px, LineDirection direction)
Sends the change endpoint command, which changes the end of an output line.
bool send_delete_object_pool() const
Sends the delete object pool message.
bool send_command(const std::vector< std::uint8_t > &data)
Sends a command to the VT server.
bool send_draw_vt_object(std::uint16_t graphicsContextObjectID, std::uint16_t VTObjectID)
Sends the draw VT object command.
GraphicMode get_graphic_mode() const
Returns the graphics mode supported by the VT server.
StateMachineState state
The current client state machine state.
std::uint8_t numberPhysicalSoftkeys
The number of physical softkeys supported by the VT server.
ProcessingFlags txFlags
A retry mechanism for internal Tx messages.
EventDispatcher< VTChangeActiveMaskEvent > & get_vt_change_active_mask_event_dispatcher()
The event dispatcher for when the active mask is changed.
std::vector< AssignedAuxiliaryInputDevice > assignedAuxiliaryInputDevices
A container to hold all auxiliary input devices known.
bool send_change_numeric_value(std::uint16_t objectID, std::uint32_t value)
Sends the change numeric value command.
SelectInputObjectOptions
Enumerates the states that can be sent with a select input object options command.
bool sendAuxiliaryMaintenance
Used internally to enable and disable cyclic sending of the auxiliary maintenance message.
bool send_change_background_colour(std::uint16_t objectID, std::uint8_t colour)
Sends the change background colour command.
FontSize get_font_or_next_smallest_font(FontSize originalFont) const
Returns the closest font to the one you passed in, in decending order.
bool send_select_input_object(std::uint16_t objectID, SelectInputObjectOptions option)
Sends a select input object command.
EventDispatcher< VTKeyEvent > & get_vt_button_event_dispatcher()
The event dispatcher for when a button is pressed or released.
bool send_draw_rectangle(std::uint16_t objectID, std::uint16_t width, std::uint16_t height)
Sends the draw rectangle command.
bool send_auxiliary_function_assignment_response(std::uint16_t functionObjectID, bool hasError, bool isAlreadyAssigned) const
Send the auxiliary control type 2 assignment reponse message.
bool send_get_number_of_softkeys() const
Sends the get number of softkeys message.
std::thread * workerThread
The worker thread that updates this interface.
MaskLockState
Denotes the lock/unlock state of a mask. Used to freeze/unfreeze rendering of a mask.
std::shared_ptr< InternalControlFunction > get_internal_control_function() const
Returns the internal control function being used by the client.
bool scale_object_pools()
Iterates through each object pool and scales each object in the pool automatically.
bool send_set_fill_attributes_object_id(std::uint16_t objectID, std::uint16_t fillAttributeobjectID)
Sends the fill attributes object id.
void terminate()
Terminates the client and joins the worker thread if applicable.
bool send_get_window_mask_data() const
Sends the get window mask data message.
bool send_get_text_font_data() const
Sends the get text font data message.
std::uint8_t get_softkey_y_axis_pixels() const
Returns the number of Y axis pixels in a softkey.
bool send_draw_line(std::uint16_t objectID, std::int16_t xOffset, std::int16_t yOffset)
Sends the draw line command.
bool send_copy_canvas_to_picture_graphic(std::uint16_t graphicsContextObjectID, std::uint16_t objectID)
Sends the copy canvas to picture graphic command.
bool send_extended_delete_version(std::array< std::uint8_t, 32 > versionLabel) const
Sends the extended delete version message.
std::uint8_t get_number_physical_softkeys() const
Returns the number of physical softkeys reported by the VT server.
std::uint32_t stateMachineTimestamp_ms
Timestamp from the last state machine update.
std::shared_ptr< PartneredControlFunction > partnerControlFunction
The partner control function this client will send to.
bool initialized
Stores the client initialization state.
bool send_erase_rectangle(std::uint16_t objectID, std::uint16_t width, std::uint16_t height)
Sends the erase rectangle command.
static void process_rx_message(const CANMessage &message, void *parentPointer)
Processes a CAN message destined for any VT client.
static constexpr std::uint32_t VT_STATUS_TIMEOUT_MS
The max allowable time between VT status messages before its considered offline.
bool send_change_child_location(std::uint16_t objectID, std::uint16_t parentObjectID, std::uint8_t relativeXPositionChange, std::uint8_t relativeYPositionChange)
Sends the change child location command.
std::uint8_t get_softkey_x_axis_pixels() const
Returns the number of X axis pixels in a softkey.
std::uint32_t lastAuxiliaryMaintenanceTimestamp_ms
The timestamp from the last time we sent the maintenance message.
std::uint32_t lastObjectPoolIndex
The last object pool index that was processed.
std::uint8_t fontStylesBitfield
The text font capabilities supported by the VT server.
bool send_delete_version(std::array< std::uint8_t, 7 > versionLabel) const
Sends the delete version message.
EventDispatcher< VTSelectInputObjectEvent > & get_vt_select_input_object_event_dispatcher()
The event dispatcher for when an input object event is triggered.
EventDispatcher< VTSelectInputObjectEvent > selectInputObjectEventDispatcher
A list of all select input object callbacks.
bool send_get_versions() const
Sends the get versions message.
bool send_change_active_mask(std::uint16_t workingSetObjectID, std::uint16_t newActiveMaskObjectID)
Sends the change active mask command.
static constexpr std::uint64_t AUXILIARY_INPUT_STATUS_DELAY_INTERACTION
The delay between the auxiliary input status messages when the input is interacted with,...
EventDispatcher< VTChangeSoftKeyMaskEvent > & get_vt_change_soft_key_mask_event_dispatcher()
The event dispatcher for when the soft key mask is changed.
bool send_get_hardware() const
Sends the get hardware message.
VTVersion
The different VT versions that a client or server might support.
@ Version3
Client or server supports all of VT version 3.
@ Version4
Client or server supports all of VT version 4.
@ Version5
Client or server supports all of VT version 5.
@ Version2OrOlder
Client or server supports VT version 2 or lower.
@ ReservedOrUnknown
Reserved value, not to be used.
@ Version6
Client or server supports all of VT version 6.
bool get_support_simultaneous_activation_physical_keys() const
Returns if the VT server supports simultaneous activation of physical keys.
bool send_auxiliary_input_status_enable_response(std::uint16_t objectID, bool isEnabled, bool hasError) const
Send the auxiliary input status type 2 enable response.
std::uint8_t largeFontSizesBitfield
The large font sizes supported by the VT server.
bool send_working_set_maintenance(bool initializing) const
Sends the working set maintenance message.
bool send_control_audio_signal(std::uint8_t activations, std::uint16_t frequency_hz, std::uint16_t duration_ms, std::uint16_t offTimeDuration_ms)
Sends the control audio signal command.
bool send_working_set_master() const
Sends the working set master message.
bool send_change_attribute(std::uint16_t objectID, std::uint8_t attributeID, std::uint32_t value)
Sends the change attribute command.
bool send_message_to_vt(const std::uint8_t *dataBuffer, std::uint32_t dataLength, CANIdentifier::CANPriority priority=CANIdentifier::CANPriority::Priority5) const
Sends a message to the VT server.
~VirtualTerminalClient()
The destructor for the VirtualTerminalClient.
EnableDisableObjectCommand
Enumerates the states that can be sent with an enable/disable object command.
A class to manage a client connection to a ISOBUS virtual terminal display.
This namespace encompasses all of the ISO11783 stack's functionality to reduce global namespace pollu...
constexpr std::uint8_t NULL_CAN_ADDRESS
The NULL CAN address defined by J1939 and ISO11783.
constexpr std::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.
constexpr std::uint64_t DEFAULT_NAME
An invalid NAME used as a default.
VirtualTerminalObjectType
The types of objects in an object pool by object type byte value.
@ ExternalReferenceNAME
Used to identify the WS Master of a Working Set that can be referenced.
@ OutputPolygon
Used to output a polygon.
@ FontAttributes
Used to group font based attributes. Can only be referenced by other objects.
@ WorkingSet
Top level object that describes an implement’s ECU or group of ECUs.
@ NumberVariable
Used to store a 32-bit unsigned integer value.
@ WindowMask
Top level object that contains other objects. The Window Mask is activated by the VT.
@ Container
Used to group objects.
@ AuxiliaryInputType1
The Auxiliary Input Type 1 object defines the designator, key number, and function type for an auxili...
@ PictureGraphic
Used to output a picture graphic (bitmap).
@ ObjectLabelRefrenceList
Used to specify an object label.
@ Macro
Special object that contains a list of commands that can be executed in response to an event.
@ ObjectPointer
Used to reference another object.
@ OutputList
Used to output a list item.
@ DataMask
Top level object that contains other objects. A Data Mask is activated by a Working Set to become the...
@ OutputEllipse
Used to output an ellipse or circle.
@ OutputString
Used to output a character string.
@ StringVariable
Used to store a fixed length string value.
@ InputNumber
Used to input an integer or float numeric.
@ OutputArchedBarGraph
Used to output an arched bar graph.
@ AuxiliaryFunctionType1
The Auxiliary Function Type 1 object defines the designator and function type for an Auxiliary Functi...
@ InputString
Used to input a character string.
@ AuxiliaryControlDesignatorType2
Used to reference Auxiliary Input Type 2 object or Auxiliary Function Type 2 object.
@ OutputRectangle
Used to output a rectangle or square.
@ Button
Used to describe a Button control.
@ Key
Used to describe a Soft Key.
@ FillAttributes
Used to group fill based attributes. Can only be referenced by other objects.
@ ExtendedInputAttributes
Used to specify a list of valid WideChars. Can only be referenced by Input Field Objects.
@ GraphicsContext
Used to output a graphics context.
@ OutputMeter
Used to output a meter.
@ ExternalObjectPointer
Used to reference an object in another Working Set.
@ AlarmMask
Top level object that contains other objects. Describes an alarm display.
@ SoftKeyMask
Top level object that contains Key objects.
@ OutputNumber
Used to output an integer or float numeric.
@ InputList
Used to select an item from a pre-defined list.
@ AuxiliaryInputType2
The Auxiliary Input Type 2 object defines the designator, key number, and function type for an Auxili...
@ AuxiliaryFunctionType2
The Auxiliary Function Type 2 object defines the designator and function type for an Auxiliary Functi...
@ LineAttributes
Used to group line based attributes. Can only be referenced by other objects.
@ OutputLine
Used to output a line.
@ ExternalObjectDefinition
Used to list the objects that may be referenced from another Working Set.
@ Animation
The Animation object is used to display simple animations.
@ KeyGroup
Top level object that contains Key objects.
@ InputBoolean
Used to input a TRUE/FALSE type input.
@ OutputLinearBarGraph
Used to output a linear bar graph.
@ ColourMap
Used to specify a colour table object.
@ InputAttributes
Used to specify a list of valid characters. Can only be referenced by input field objects.
bool(*)(std::uint32_t callbackIndex, std::uint32_t bytesOffset, std::uint32_t numberOfBytesNeeded, std::uint8_t *chunkBuffer, void *parentPointer) DataChunkCallback
A callback to get chunks of data for transfer by a protocol.
AcknowledgementType
The types of acknowledgement that can be sent in the Ack PGN.
@ Negative
"NACK" Indicates the request was not completed or we do not support the PGN
A struct for storing information about an auxiliary input device.
Struct for storing the state of an auxiliary input on our device.
An object for storing information regarding an object pool upload.
std::uint32_t autoScaleSoftKeyDesignatorOriginalHeight
The original height of a soft key designator as designed in the pool (in pixels)
const std::uint8_t * objectPoolDataPointer
A pointer to an object pool.
std::string versionLabel
An optional version label that will be used to load/store the pool to the VT. 7 character max!
bool useDataCallback
Determines if the client will use callbacks to get the data in chunks.
DataChunkCallback dataCallback
A callback used to get data in chunks as an alternative to loading the whole pool at once.
const std::vector< std::uint8_t > * objectPoolVectorPointer
A pointer to an object pool (vector format)
std::uint32_t autoScaleDataMaskOriginalDimension
The original length or width of this object pool's data mask area (in pixels)