Server API

Overview

Creating a task controller server which does data logging, mapping, section control, and other common ISOBUS things is a very complex task.

AgIsoStack++ provides an abstract server API to make the CAN portion of this task easier. It allows you to create a task controller server by implementing a few virtual functions, and handles most of the CAN communication for you.

Features:

  • Supports the CAN messaging needed for TC-GEO, TC-SC, and TC-BAS

  • Manages DDOP transfers for you, and abstracts the client connection process so that you can focus on your application logic

  • Supports both version 3 and version 4 of the TC standard in ISO11783-10

  • Manages the TC status message

  • Tracks client connection status and timeouts

  • Integrates well with our Device Descriptor Object Pool Classes for access to product and implement information

  • Provides a simple interface for sending commands and receiving values from the client

  • Includes some helpers for parsing a DDOP, which can tell you the implement geometry and product information quickly without needing to write a lot of code.

  • Sends all the required responses to the clients on your behalf, so you don’t need to populate messages yourself.

The functions you need to implement in order to get the TC server working are (in no particular order):

  • activate_object_pool
    • This will be called by the server when a valid, connected client requests that the server activate the latest DDOP it uploaded.

    • When this function is called, you should parse the DDOP, potentially using our DDOP classes. (if you have previously received one via a call to store_device_descriptor_object_pool).

    • You should then “activate” the DDOP by doing whatever is necessary to make your application use the new DDOP (this is application specific for your task controller).

    • You should return true here if the DDOP was valid and activated, and false if it was not valid.

    • If you return false, you must populate the error parameters with a reason why the DDOP was not valid.

  • change_designator
    • This will be called by the server when a valid, connected client requests that the server change a designator for something in the DDOP.

    • This is a common way to change the language of the DDOP, rename the implement, or change the displayed product name.

    • You should implement this function to change the designator of the object and return whether or not it was successful.

  • deactivate_object_pool
    • This will be called by the server when a valid, connected client requests that the server deactivate its currently active DDOP.

    • When this function is called, you should deactivate the DDOP by doing whatever is necessary to make your application stop using the DDOP (this is application specific for your task controller).

    • You should return true here if the DDOP was deactivated, and false if it was not valid.

    • If you return false, you must populate the error parameter with a reason why the request failed.

  • get_is_stored_device_descriptor_object_pool_by_structure_label
    • Part of the connection process with the client involves the client possibly asking the server if it already has a DDOP with a certain structure label, associated to that client’s NAME.

    • This function should return true if the server has a DDOP with the given structure label for that client’s NAME already stored in non-volatile memory, and false if it does not.

    • Note that the structure label will always be 7 bytes long, and the extended structure label is optional, and may be empty or up to 32 bytes long.

    • Both labels should match exactly for the function to return true.

    • If no extended label is provided, it may be ignored.

    • Note that normally a TC should only store the latest DDOP, and not multiple DDOPs with the same structure label(s).

  • get_is_stored_device_descriptor_object_pool_by_localization_label
    • Part of the connection process with the client involves the client possibly asking the server if it already has a DDOP with a certain localization label, associated to that client’s NAME.

    • The localization label describes the units, language, and country of the DDOP.

    • This function should return true if the server has a DDOP with the given localization label for that client’s NAME already stored in non-volatile memory, and false if it does not.

    • Note that the localization label will always be 7 bytes long.

    • Note that normally a TC should only store the latest DDOP, and not multiple DDOPs with the same localization label.

  • get_is_enough_memory_available
    • This function will be called to determine if the server has enough memory (both RAM and ROM) to store a DDOP that a client wants to transfer.

    • Generally, the server should return true if it has enough memory to store the DDOP, and false if it does not.

    • Returning a value of true indicates “There may be enough memory available. However, because there is overhead associated with object storage,it is impossible to predict whether there is enough memory available.” and false indicates “There is not enough memory available. Do not transmit device descriptor object pool.”

  • identify_task_controller
    • This function will be called if someone requests that the TC identify itself. If this gets called, you should display the TC number for 3 seconds if your TC has a visual interface.

  • on_client_timeout
    • This function will be called by the server when a connected client times out.

    • You should implement this function to do whatever you want to do when a client times out.

    • Generally this means you will want to also deactivate the DDOP for that client.

  • on_process_data_acknowledge
    • This function will be called by the server when a client sends an acknowledgement for a process data command that was sent to it.

    • This can be useful to know if the client received the last command you sent to it, or not, when using the set_value_and_acknowledge function.

  • on_value_command
    • This function will be called by the server when a client sends a value command to the TC. This is the main way the client will provide you with data!

    • You should implement this function to do whatever you want to do when a client sends a value command. This could be anything from setting a value in your program, logging the value to a file, drawing something on a map, or sending a command to a connected implement.

    • The client could be telling you that a section’s state changed, or that a boom’s position changed, etc. Therefore this is probably the most important function to implement to get your TC “working”. Use the ISOBUS data dictionary to determine what the parameters mean.

  • store_device_descriptor_object_pool
    • This function is called when the server wants you to save a DDOP to non volatile memory (NVM).

    • You should implement this function to save the DDOP to NVM.

    • If appendToPool is true, you should append the DDOP to the existing DDOP in NVM. Clients may send DDOPs in may different parts so it is imperative that you handle this correctly.

API

class TaskControllerServer

An ISO11783-10 task controller (or data logger) server. A task controller collects task data from connected implements, and optionally controls them. This interface supports the CAN layer of TC-SC, TC-GEO, and TC-BAS.

Public Types

enum class ObjectPoolActivationError : std::uint8_t

Enumerates the different error codes that can be returned when activating a device descriptor object pool.

Values:

enumerator NoErrors
enumerator ThereAreErrorsInTheDDOP
enumerator TaskControllerRanOutOfMemoryDuringActivation
enumerator AnyOtherError
enumerator DifferentDDOPExistsWithSameStructureLabel
enum class ObjectPoolDeletionErrors : std::uint8_t

Enumerates the different error codes that can be returned when deleting a device descriptor object pool.

Values:

enumerator ObjectPoolIsReferencedByTaskData
enumerator ServerCannotCheckForObjectPoolReferences
enumerator ErrorDetailsNotAvailable
enum class ObjectPoolErrorCodes : std::uint8_t

Enumerates the different error codes that can be returned when processing a DDOP.

Values:

enumerator NoErrors
enumerator MethodOrAttributeNotSupported
enumerator UnknownObjectReference
enumerator AnyOtherError
enumerator DDOPWasDeletedFromVolatileMemory
enum class ProcessDataCommands : std::uint8_t

Enumerates the different process data commands that can be sent to the client.

Values:

enumerator TechnicalCapabilities

Used for determining the technical capabilities of a TC, DL, or client.

enumerator DeviceDescriptor

Transfer and management of device descriptors.

enumerator RequestValue

Used when the value of the data entity specified by the data dictionary identifier is requested.

enumerator Value

This command is used both to answer a request value command and to set the value of a process data entity.

enumerator MeasurementTimeInterval

The process data value is the time interval for sending the data element specified by the data dictionary identifier.

enumerator MeasurementDistanceInterval

The process data value is the distance interval for sending the data element specified by the data dictionary identifier.

enumerator MeasurementMinimumWithinThreshold

The client has to send the value of this data element to the TC or DL when the value is higher than the threshold value.

enumerator MeasurementMaximumWithinThreshold

The client has to send the value of this data element to the TC or DL when the value is lower than the threshold value.

enumerator MeasurementChangeThreshold

The client has to send the value of this data element to the TC or DL when the value change is higher than or equal to the change threshold since last transmission.

enumerator PeerControlAssignment

This message is used to establish a connection between a setpoint value source and a setpoint value user.

enumerator SetValueAndAcknowledge

This command is used to set the value of a process data entity and request a reception acknowledgement from the recipient.

enumerator Reserved

Reserved.

enumerator Reserved2

Reserved.

enumerator Acknowledge

Message is a Process Data Acknowledge (PDACK).

enumerator Status

Message is a Task Controller Status message.

enumerator ClientTask

Sent by the client as a status message every 2s.

enum class ServerOptions : std::uint8_t

Enumerates the different options that can be reported by the server. Each option is a bit in a bitfield, with 1 meaning the option is supported and 0 meaning it is not. For example, if the server supports documentation and peer control assignment, but not the other options, the bitfield would be 0b00001001.

Values:

enumerator SupportsDocumentation
enumerator SupportsTCGEOWithoutPositionBasedControl
enumerator SupportsTCGEOWithPositionBasedControl
enumerator SupportsPeerControlAssignment
enumerator SupportsImplementSectionControl
enumerator ReservedOption1
enumerator ReservedOption2
enumerator ReservedOption3
enum class ProcessDataAcknowledgeErrorCodes : std::uint8_t

Enumerates all PDACK error codes that can be sent to or from the client.

Values:

enumerator ProcessDataCommandNotSupported
enumerator InvalidElementNumber
enumerator DDINotSupportedByElement
enumerator TriggerMethodNotSupported
enumerator ProcessDataNotSettable
enumerator InvalidOrUnsupportedIntervalOrThreshold
enumerator ProcessDataValueDoesNotConformToDDIDefinition
enumerator ProcessDataValueIsOutOfOperationalRangeOfThisDevice
enum class TaskControllerVersion : std::uint8_t

Enumerates the different versions of the task controller standard.

Values:

enumerator DraftInternationalStandard

The version of the DIS (draft International Standard).

enumerator FinalDraftInternationalStandardFirstEdition

The version of the FDIS.1 (final draft International Standard, first edition).

enumerator FirstPublishedEdition

The version of the FDIS.2 and the first edition published ss an International Standard.

enumerator SecondEditionDraft

The version of the second edition published as a draft International Standard(E2.DIS).

enumerator SecondPublishedEdition

The version of the second edition published as the final draft International Standard(E2.FDIS) and as the International Standard(E2.IS)

enumerator Unknown

Public Functions

TaskControllerServer(std::shared_ptr<InternalControlFunction> internalControlFunction, std::uint8_t numberBoomsSupported, std::uint8_t numberSectionsSupported, std::uint8_t numberChannelsSupportedForPositionBasedControl, const TaskControllerOptions &options, TaskControllerVersion versionToReport = TaskControllerVersion::SecondPublishedEdition)

Constructor for a TC server.

Parameters:
  • internalControlFunction[in] The control function to use to communicate with the clients.

  • numberBoomsSupported[in] The number of booms to report as supported by the TC.

  • numberSectionsSupported[in] The number of sections to report as supported by the TC.

  • numberChannelsSupportedForPositionBasedControl[in] The number of channels to report as supported by the TC.

  • options[in] The options to report as supported by the TC. See the TaskControllerOptions object for more info.

  • versionToReport[in] The version of the task controller standard to report as supported by the TC. Generally you should leave this as 4 (SecondPublishedEdition).

virtual ~TaskControllerServer()

Destructor for a TC server.

TaskControllerServer(TaskControllerServer&) = delete

Deleted copy constructor.

TaskControllerServer &operator=(const TaskControllerServer&) = delete

Deleted assignment operator.

virtual bool activate_object_pool(std::shared_ptr<ControlFunction> clientControlFunction, ObjectPoolActivationError &activationError, ObjectPoolErrorCodes &objectPoolError, std::uint16_t &parentObjectIDOfFaultyObject, std::uint16_t &faultyObjectID) = 0

This function will be called by the server when the client wants to activate its DDOP. You should implement this function to activate the DDOP and return whether or not it was successful. Generally this means that you will want to parse the pool, and make sure its schema is valid at this time. You can use our DeviceDescriptorObjectPool class to help you with this.

Parameters:
  • clientControlFunction[in] The control function which is requesting the activation.

  • activationError[out] The error code to return if the activation fails.

  • objectPoolError[out] This error code tells the client if there was an error in the DDOP.

  • parentObjectIDOfFaultyObject[out] If there was an error in the DDOP, this is the parent object ID of the object that caused the error. Otherwise you should return 0xFFFF

  • faultyObjectID[out] If there was an error in the DDOP, this is the object ID of the object that caused the error. Otherwise you should return 0xFFFF

Returns:

Whether or not the activation was successful.

virtual bool change_designator(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t objectIDToAlter, const std::vector<std::uint8_t> &designator) = 0

This function will be called by the server when the client wants to change the designator of an object. This could be called because the client wants to change the name of an implement, or the name of a section, or change the active language being used in the DDOP’s designators. You should implement this function to change the designator of the object and return whether or not it was successful.

Parameters:
  • clientControlFunction[in] The control function which is requesting the designator change.

  • objectIDToAlter[in] The object ID of the object to change the designator of.

  • designator[in] The new designator to set for the object.

Returns:

Whether or not the designator change was successful.

virtual bool deactivate_object_pool(std::shared_ptr<ControlFunction> clientControlFunction) = 0

This function will be called by the server when the client wants to deactivate its DDOP. You should implement this function to deactivate the DDOP and return whether or not it was successful.

Parameters:

clientControlFunction[in] The control function which is requesting the deactivation.

Returns:

Whether or not the deactivation was successful.

virtual bool delete_device_descriptor_object_pool(std::shared_ptr<ControlFunction> clientControlFunction, ObjectPoolDeletionErrors &returnedErrorCode) = 0

This function will be called by the server when the client wants to delete its DDOP. Each client is allowed to have one DDOP, so deletion is not required, but a client might be kind and delete its DDOP when it is no longer needed. You should implement this function to delete the DDOP and return whether or not it was successful.

Parameters:
  • clientControlFunction[in] The control function which is requesting the deletion.

  • returnedErrorCode[out] The error code to return if the deletion fails.

Returns:

Whether or not the deletion was successful.

virtual bool get_is_stored_device_descriptor_object_pool_by_structure_label(std::shared_ptr<ControlFunction> clientControlFunction, const std::vector<std::uint8_t> &structureLabel, const std::vector<std::uint8_t> &extendedStructureLabel) = 0

This function will be called by the server when the server needs to know if it has previously saved to non volatile memory (NVM) a DDOP which is identified by the provided structure label, and optionally also the provided extended structure label. You should implement this function to return whether or not the DDOP is stored in NVM.

Parameters:
  • clientControlFunction[in] The control function which is requesting the information.

  • structureLabel[in] The structure label of the DDOP to check for. (always 7 bytes)

  • extendedStructureLabel[in] The extended structure label of the DDOP to check for. (up to 32 bytes)

Returns:

Whether or not the DDOP is stored in NVM.

virtual bool get_is_stored_device_descriptor_object_pool_by_localization_label(std::shared_ptr<ControlFunction> clientControlFunction, const std::array<std::uint8_t, 7> &localizationLabel) = 0

This function will be called by the server when the server needs to know if it has previously saved to non volatile memory (NVM) a DDOP which is identified by the provided localization label. You should implement this function to return whether or not the DDOP is stored in NVM.

Parameters:
  • clientControlFunction[in] The control function which is requesting the information.

  • localizationLabel[in] The localization label of the DDOP to check for.

Returns:

Whether or not the DDOP is stored in NVM.

virtual bool get_is_enough_memory_available(std::uint32_t numberBytesRequired) = 0

This function will be called by the server when the client wants to transfer its DDOP to the server and needs to know if the server has enough memory available to store the DDOP. You should implement this function to return whether or not the server has enough memory available to store the DDOP.

Parameters:

numberBytesRequired[in] The number of bytes required to store the DDOP.

Returns:

Whether or not the server has enough memory available to store the DDOP. A value of true indicates: “There may be enough memory available. However,

because there is overhead associated with object storage,it is impossible to predict whether there is enough memory available.” and false indicates: “There is not enough memory available. Do not transmit device descriptor object pool.”

virtual void identify_task_controller(std::uint8_t taskControllerNumber) = 0

This function will be called if someone requests that the TC identify itself. If this gets called, you should display the TC number for 3 seconds if your TC has a visual interface.

Parameters:

taskControllerNumber[in] The task controller number to display.

virtual void on_client_timeout(std::shared_ptr<ControlFunction> clientControlFunction) = 0

This function will be called by the server when a connected client times out. You should implement this function to do whatever you want to do when a client times out. Generally this means you will want to also deactivate the DDOP for that client.

Parameters:

clientControlFunction[in] The control function which timed out.

virtual void on_process_data_acknowledge(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint8_t errorCodesFromClient, ProcessDataCommands processDataCommand) = 0

This function will be called by the server when a client sends an acknowledgement for a process data command that was sent to it. This can be useful to know if the client received the command or not when using the set_value_and_acknowledge command.

Parameters:
  • clientControlFunction[in] The control function which sent the acknowledgement.

  • dataDescriptionIndex[in] The data description index of the data element that was acknowledged.

  • elementNumber[in] The element number of the data element that was acknowledged.

  • errorCodesFromClient[in] The error codes that the client sent in the acknowledgement. This will be a bitfield defined by the ProcessDataAcknowledgeErrorCodes enum.

  • processDataCommand[in] The process data command that was acknowledged.

virtual bool on_value_command(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::int32_t processDataValue, std::uint8_t &errorCodes) = 0

This function will be called by the server when a client sends a value command to the TC. You should implement this function to do whatever you want to do when a client sends a value command. This could be anything from setting a value in your program, to sending a command to a connected implement. The client could be telling you that a section’s state changed, or that a boom’s position changed, etc. Therefore this is probably the most important function to implement to get your TC “working”. Use the ISOBUS data dictionary to determine what the dataDescriptionIndex and elementNumber mean.

Parameters:
  • clientControlFunction[in] The control function which sent the value command.

  • dataDescriptionIndex[in] The data description index of the data element that was sent.

  • elementNumber[in] The element number of the data element that was sent.

  • processDataValue[in] The process data value that was sent.

  • errorCodes[out] You should return any errors that occurred while processing the value command in this variable as defined by the ProcessDataAcknowledgeErrorCodes enum. This will be sent back to the client if an acknowledgement is requested.

Returns:

Whether or not the value command was processed successfully.

virtual bool store_device_descriptor_object_pool(std::shared_ptr<ControlFunction> clientControlFunction, const std::vector<std::uint8_t> &objectPoolData, bool appendToPool) = 0

This function is called when the server wants you to save a DDOP to non volatile memory (NVM). You should implement this function to save the DDOP to NVM. If appendToPool is true, you should append the DDOP to the existing DDOP in NVM.

Parameters:
  • clientControlFunction[in] The control function which is requesting the save.

  • objectPoolData[in] The DDOP itself as a binary blob.

  • appendToPool[in] Whether or not to append the DDOP to the existing DDOP in NVM, or overwrite it.

Returns:

Whether or not the save was successful.

bool send_request_value(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber) const

Sends a request to a client for an element’s value of a particular DDI.

Parameters:
  • clientControlFunction[in] The control function to send the message to

  • dataDescriptionIndex[in] The Data Description Index being requested

  • elementNumber[in] The element number being requested

Returns:

true if the message was sent, otherwise false

bool send_time_interval_measurement_command(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t timeInterval) const

Sends a time interval measurement command. The process data value for this command is the time interval for sending the data element specified by the data dictionary identifier.The client has to send the value of this data element to the TC or DL cyclic with this time interval.

Parameters:
  • clientControlFunction[in] The control function to send the message to

  • dataDescriptionIndex[in] The data description index of the data element to send the command for

  • elementNumber[in] The element number of the data element to send the command for

  • timeInterval[in] The time interval for sending the data element specified by the data dictionary identifier.

Returns:

true if the message was sent, otherwise false

bool send_distance_interval_measurement_command(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t distanceInterval) const

Sends a distance interval measurement command. The process data value for this command is the distance interval for sending the data element specified by the data dictionary identifier.The client has to send the value of this data element to the TC or DL cyclic with this distance interval.

Parameters:
  • clientControlFunction[in] The control function to send the message to

  • dataDescriptionIndex[in] The data description index of the data element to send the command for

  • elementNumber[in] The element number of the data element to send the command for

  • distanceInterval[in] The distance interval for sending the data element specified by the data dictionary identifier.

Returns:

true if the message was sent, otherwise false

bool send_minimum_threshold_measurement_command(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t minimum) const

Sends a minimum threshold measurement command. The process data value for this command is the minimum threshold for sending the data element specified by the data dictionary identifier.The client has to send the value of this data element to the TC or DL when the value is higher than the threshold value.

Parameters:
  • clientControlFunction[in] The control function to send the message to

  • dataDescriptionIndex[in] The data description index of the data element to send the command for

  • elementNumber[in] The element number of the data element to send the command for

  • minimum[in] The minimum threshold for sending the data element specified by the data dictionary identifier.

Returns:

true if the message was sent, otherwise false

bool send_maximum_threshold_measurement_command(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t maximum) const

Sends a maximum threshold measurement command. The process data value for this command is the maximum threshold for sending the data element specified by the data dictionary identifier.The client has to send the value of this data element to the TC or DL when the value is lower than the threshold value.

Parameters:
  • clientControlFunction[in] The control function to send the message to

  • dataDescriptionIndex[in] The data description index of the data element to send the command for

  • elementNumber[in] The element number of the data element to send the command for

  • maximum[in] The maximum threshold for sending the data element specified by the data dictionary identifier.

Returns:

true if the message was sent, otherwise false

bool send_change_threshold_measurement_command(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t threshold) const

Sends a change threshold measurement command. The process data value for this command is the change threshold for sending the data element specified by the data dictionary identifier.The client has to send the value of this data element to the TC or DL when the value change is higher than or equal to the change threshold since last transmission.

Parameters:
  • clientControlFunction[in] The control function to send the message to

  • dataDescriptionIndex[in] The data description index of the data element to send the command for

  • elementNumber[in] The element number of the data element to send the command for

  • threshold[in] The change threshold for sending the data element specified by the data dictionary identifier.

Returns:

true if the message was sent, otherwise false

bool send_set_value_and_acknowledge(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t processDataValue) const

Sends a set value and acknowledge command. This command is used to set the value of a process data entity and request a reception acknowledgement from the recipient. The set value command process data value is the value of the data entity specified by the data dictionary identifier.

Parameters:
  • clientControlFunction[in] The control function to send the message to

  • dataDescriptionIndex[in] The data description index of the data element to send the command for

  • elementNumber[in] The element number of the data element to send the command for

  • processDataValue[in] The process data value to send

Returns:

true if the message was sent, otherwise false

bool send_set_value(std::shared_ptr<ControlFunction> clientControlFunction, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint32_t processDataValue) const

Sends a set value command without requesting an acknowledgement. This command is used to set the value of a process data entity. The set value command process data value is the value of the data entity specified by the data dictionary identifier.

Parameters:
  • clientControlFunction[in] The control function to send the message to

  • dataDescriptionIndex[in] The data description index of the data element to send the command for

  • elementNumber[in] The element number of the data element to send the command for

  • processDataValue[in] The process data value to send

Returns:

true if the message was sent, otherwise false

void set_task_totals_active(bool isTaskActive)

Use this to set the reported task state in the status message. Basically, this should be set to true when the user starts a job, and false when the user stops a job.

Note

Don’t be like some terminals which set this to true all the time, that’s very annoying for the client.

Parameters:

isTaskActive[in] Whether a task is currently active or not.

bool get_task_totals_active() const

Returns whether a task is currently active or not.

Returns:

Whether a task is currently active or not.

LanguageCommandInterface &get_language_command_interface()

Returns the language command interface used to communicate with the client which language/units are in use. The language command is very important for the TC to function correctly, so it is recommended that you call this function and configure the language command interface before calling initialize().

Returns:

The language command interface used to communicate with the client which language/units are in use.

std::condition_variable &get_condition_variable()

Returns a condition variable which you can optionally use to wake up your server’s thread when messages are received from the client.

Returns:

A condition variable which you can optionally use to wake up your server’s thread

void initialize()

Initializes the task controller server.

bool get_initialized() const

Returns whether or not the task controller server has been initialized.

Returns:

Whether or not the task controller server has been initialized.

void terminate()

Shuts down the TC server, unregisters PGN callbacks.

void update()

This must be called periodically for the interface to operate correctly.

This function must be called periodically. You have some choices on how to do this: First, you could poll it at a high rate in your main thread, at least 2-3x as fast as your fastest triggered message. Second, you could call it at a slower rate (something like 250-500 ms), and update it when the condition variable is notified. You can get the condition variable by calling get_condition_variable() if threading is enabled in the CAN stack. Third, you could run this in a separate thread, but again, you should call it at least 2-3x as fast as your fastest triggered message. Calling this often ensures timed out clients are pruned, and messages are processed in a timely fashion, which is important for the TC to function correctly and for agronomic/implement performance.