Hello World 3

Introduction

As you have seen in these tutorials, Component Types have many different features which can be used when building flight software. Using the right features, and combining them in the right way, is key to producing powerful, flexible Component Types.

In this tutorial you will develop a new Component Type which will regularly report some information to the ground. It will do this autonomously, without an operator requesting it via Actions or Parameters.

You will implement this Component Type using some features you’ve already seen and some new ones which will be introduced along the way:

  • The desired information will be delivered to the ground by Publishing Events.

  • To Publish these Events regularly and autonomously you’ll use a Task.

  • You’ll then use a Mutable Parameter, introduced in the previous tutorial, to control what information is delivered, and when.

In this tutorial, you will:

  • Learn about Tasks.

  • Learn about Events.

  • Gain more familiarity with the development workflow for Component Types and Deployment Types.

  • Develop a sense for how more complex problems can be decomposed and solved using Flightkit.

Before You Begin

Guidance on the styles and conventions used in these tutorials is available here.

Unless otherwise directed, all commands in this tutorial must be run in the the_basics/HelloWorld3 workspace.

You must have Flightkit installed as described in the Getting Started Guide.

Navigate to the the_basics/HelloWorld3 workspace in a terminal and run hfk workspace prepare to ensure it is ready to use.

Tasks

Flightkit enables the flight software you build to be naturally portable across different hardware platforms and operating systems.

This is achieved in part by using a consistent model of an execution context called a Task.

As you might expect, Tasks are part of the definition of a Component Type.

There are 3 kinds of Tasks, each with their own defined behaviour:

  • Periodic Tasks are triggered each time a fixed time interval has elapsed.

  • Sporadic Tasks are triggered when data is added to their associated queue.

  • Interrupt Tasks are triggered when a hardware interrupt occurs.

In this tutorial you will use a Periodic Task.

We use Periodic Tasks whenever periodic behaviour is required in our Component Types, examples include:

  • The cdh.auto.Schedule Component Type, which makes use of a Periodic Task to autonomously execute activity defined in a schedule uplinked by operators.

  • The cdh.logging.DataLogger Component Type, which carries out periodic data logging, and uses a pair of Periodic Tasks to do so in a way that is robust to IO delays.

  • The cdh.tm.Beacon Component Type, which uses a Periodic Task to regularly deliver housekeeping data in telemetry packets.

In this section of the tutorial you’ll add a Periodic Task named publishEvent to the provided EventPublisher Component Type, and test it using the provided EventPublisherDemo Deployment Type.

Adding the publishEvent Task

To add a Periodic Task to a Component Type, you must add a <PeriodicTask> element to its componentType.xml.

EventPublisher currently has no Tasks, so in this case you will also need a <Tasks> element to contain the <PeriodicTask> element.

Open the EventPublisher componentType.xml. Add the following below the <!-- Tasks --> comment:

<Tasks>
  <PeriodicTask name="publishEvent" defaultPeriod="3.0">
    <Description>
      A task used to periodically publish events
    </Description>
  </PeriodicTask>
</Tasks>

This adds a new Periodic Task named publishEvent to the Component Type, with a default period of 3 seconds. This period can be overridden when the EventPublisher Component Type is instantiated.

Having updated the componentType.xml, you now need to generate the Component Type.

Generate the Component Type using the following command:

hfk component-type generate EventPublisher

This creates a Task function in the Component Type's template file called EventPublisher_taskPublishEvent().

Copy this function, along with its banner comment, and paste it into EventPublisher.c below EventPublisher_localFini() to maintain the function order.

Replace the TODO in the Task function with a call to EVENTPUBLISHER_CONTAINER_LOG_INFO() so that a message will be printed to the terminal when the Task executes.

Show the updated task code
/*---------------------------------------------------------------------------*
 * A task used to periodically publish events
 *---------------------------------------------------------------------------*/

void EventPublisher_taskPublishEvent
(
    EventPublisher_t *pt_EventPublisher
)
{
-   /* TODO: Insert task code here */
+   EVENTPUBLISHER_CONTAINER_LOG_INFO(
+       pt_EventPublisher, "EventPublisher.publishEvent executed!");
}

Updating the EventPublisherDemo Deployment Type

In the previous section you added a new Task to the Component Type. A Component Type is a blueprint for Component Instances, so every Component Instance must also now include the new Task.

There is already an instance of the EventPublisher Component Type in the EventPublisherDemo Deployment Type. You will now update the Deployment Type so that you can see the new Task execute.

Open library/deployment_types/HelloWorld3/deploymentType.xml and update the MyEventPublisher Component Instance to include the new publishEvent Task.

Show the updated MyEventPublisher Component Instance
      <Component name="MyEventPublisher" type="EventPublisher">
        <Description>
          An instance of the EventPublisher which will periodically publish events
        </Description>
+       <Tasks>
+         <PeriodicTask name="publishEvent" priority="0"/>
+       </Tasks>
      </Component>

When adding a Task to a Component Instance you must also specify its priority. This sets the priority that the OSAL (Operating System Abstraction Layer) will apply to this Task when running multiple Tasks concurrently. The higher the number, the higher the priority.

On Linux systems we leave scheduling decisions up to the Linux scheduler. That means this value is ignored in this case, so we have set it to "0".

We could have also specified a period which would take precedence over the defaultPeriod specified in the componentType.xml. Omitting this attribute results in the defaultPeriod being used.

To test the new Task, build the Mission and run the Deployment Instance using the same approach as previous tutorials:

hfk mission build HelloWorld3
./output/missions/HelloWorld3/EventPublisherDemo/Default/Default

You should see the following message being printed to the terminal every three seconds:

INF: EventPublisher.c:80 MyEventPublisher: EventPublisher.publishEvent executed!

Once you are happy the Task is working as it should, terminate the Deployment Instance in the terminal.

Events

In Flightkit Events are used for asynchronous signalling by Component Types. Component Types may Publish Events (produce them) and Subscribe to Events (receive them). There are usually more Component Types Publishing Events than Subscribing to them.

Events are used for many different purposes, examples include:

  • Publishing an Event when a mode manager enters safe mode.

  • Publishing an Event when execution of an onboard schedule completes, or fails.

  • Publishing an Event when some Parameter value exceeds pre-defined limits.

In this section of the tutorial you will define a new Event, and learn how to Publish it from the Task you added in the previous section.

Adding the SomethingHappened Event

To add an Event to a Component Type, you must add an <Event> element to its componentType.xml.

EventPublisher currently has no Events, so in this case you will also need an <Events> element to contain the <Event> element.

Open the EventPublisher componentType.xml. Add the following below the <!-- Events --> comment:

<Events defaultBaseId="10000">
  <Event name="SomethingHappened" severity="info">
    <Description>
      An event to notify the operator that something happened!
    </Description>
  </Event>
</Events>

Each Event has a numeric identifier associated with it, and the defaultBaseId is the identifier used by the first Event defined in this Component Type. Each subsequent Event is numbered sequentially from defaultBaseId.

defaultBaseId should be set such that each of the Events defined by the Component Type will be given a unique identifier when used in a Deployment Type.

A value of 10000 here is sufficiently unique for our purposes.

When defining an Event you must also specify its severity. The lowest severity is info and is appropriate for this tutorial.

Generate the Component Type using the same command as before.

This creates a new header file in library/component_types/EventPublisher/include/type/ named EventPublisher_events.h, containing the symbol which defines the new Event's identifier.

You will now see how the new Event can be Published.

Using EDS

In Flightkit, Services are a way of formally describing the connections between Component Types. You will learn more about Services in the later Using A Service tutorial.

For now it is sufficient to know that, in order to Publish or Subscribe to an Event, Component Types must use the Event Distribution Service (EDS). EDS is the Service which defines the Event Message. It is an example of a Publish-Subscribe Service.

To Publish Event Messages from the EventPublisher Component Type you must add a <Services><Published><Service> element to its componentType.xml.

Open the EventPublisher componentType.xml. Add the following below the <!-- Services (Provided, Required, Subscribed, Brokered and Published) --> comment:

<Services>
  <Published>
    <Service name="task" type="component.EDS">
      <Description>
        A publication for publishing Event messages from the publishEvent
        periodic task
      </Description>
    </Service>
  </Published>
</Services>

Generate the EventPublisher Component Type.

This generates a new function that you can call from the EventPublisher Component Implementation to Publish Events. Its prototype can be found in EventPublisher_EDSPublisher_package.h:

/**
 * Publish the task EDS event message
 * A message publisher for publishing EDS.Event messages from the publishEvent periodic task
 * @param pt_EventPublisher The EventPublisher component
 * @param t_EventId The Event ID
 * @param u32_Status A status (exception code) associated with an event.
 * @param pu8_Info An optional blob of information.
 * @param u8_InfoLength The length of the data in the buffer, in bytes
 */
void EventPublisher_EDSPublisher_publishEventTask
(
    EventPublisher_t *pt_EventPublisher,
    EDS_EventId_t t_EventId,
    uint32_t u32_Status,
    const uint8_t *pu8_Info,
    uint8_t u8_InfoLength
);

You will now call this function from the publishEvent Task to Publish an Event periodically.

Publishing the SomethingHappened Event

Include the EventPublisher_EDSPublisher_package.h header in your EventPublisher.c source file.

Show the added #include directive
#include "types.h"
#include "status.h"
#include "EventPublisher.h"
#include "container/EventPublisher_Container_package.h"
+#include "container/EventPublisher_EDSPublisher_package.h"
#include "EventPublisher_config.h"
#include "Task_ProtectionLock.h"
#include "Util_Log.h"

To Publish the SomethingHappened Event from the publishEvent Task, update the Task function to call EventPublisher_EDSPublisher_publishEventTask(), passing in EVENTPUBLISHER_EVENTID_SOMETHING_HAPPENED as the Event ID argument.

Show the updated task code
void EventPublisher_taskPublishEvent
(
    EventPublisher_t *pt_EventPublisher
)
{
    EVENTPUBLISHER_CONTAINER_LOG_INFO(
        pt_EventPublisher, "EventPublisher.publishEvent executed!");
+
+   EventPublisher_EDSPublisher_publishEventTask(
+       pt_EventPublisher,
+       EVENTPUBLISHER_EVENTID_SOMETHING_HAPPENED,
+       STATUS_SUCCESS,
+       NULL,
+       0);
}

Notice that, as well as an Event ID, the EventPublisher_EDSPublisher_publishEventTask() function allows you to provide u32_Status and pu8_Info arguments. These allow you to add more detailed information to the Published Event.

u32_Status is typically used to indicate what kind of failure caused the Event to be Published. For Events not caused by failures, STATUS_SUCCESS is appropriate for this argument.

pu8_Info allows you to include up to 8 arbitrary bytes of data in the Event. If you do not wish to pass any additional information pass NULL and a length of 0.

Having added an EDS Publication to EventPublisher you now need to update the EventPublisherDemo Deployment Type again. You’ll do that in the next section.

Updating the EventPublisherDemo Deployment Type

As described above, Publish-Subscribe Services like EDS define Messages which can be Published and Subscribed to by Component Types.

Component Types which Publish Messages are referred to as Publishers. Those which Subscribe to Messages are referred to as Subscribers.

Component Types may also Broker Messages. Brokers are responsible for receiving Messages from Publishers and dispatching them to Subscribers. As a result, Publishers, like the EventPublisher Component Type, must be connected to Brokers when used in Deployment Types.

Open library/deployment_types/EventPublisherDemo/deploymentType.xml

Notice that an instance of the InContextEDSBroker Component Type is included in EventPublisherDemo Deployment Type. This is an EDS Broker which we provide in the FlightkitFoundation bundle.

To test the EventPublisher Component Type you need to connect MyEventPublisher Component Instance to the InContextEDSBroker Component Instance.

Add the following <Connections><Services> element to MyEventPublisher.

Show the updated MyEventPublisher Component Instance
      <Component name="MyEventPublisher" type="EventPublisher">
        <Description>
          An instance of the EventPublisher which will periodically publish events
        </Description>
+       <Connections><Services>
+         <Service name="task" component="core.InContextEDSBroker" service="eventBroker"/>
+       </Services></Connections>
        <Tasks>
          <PeriodicTask name="publishEvent" priority="0"/>
        </Tasks>
      </Component>

When making a Publish-Subscribe Service connection like this you must always specify:

  • The name of the Service Publication on the connecting Component Instance (the Publisher or Subscriber) using the name attribute. In this case name is task, as that is the name of the EDS Publication in the EventPublisher componentType.xml.

  • The name of the Component Instance serving as the Broker using the component attribute. In this case component is core.InContextEDSBroker, as that is the name of the desired Broker.

  • The name of the Service Brokerage on the Broker being connected to using the service attribute. In this case service is eventBroker, as that is the name of the InContextEDSBroker Component Type's EDS Brokerage.

To test that MyEventPublisher now correctly Publishes Events via the InContextEDSBroker you need to build the HelloWorld3 Mission and run the Default Deployment Instance as before.

You will see the same message as before being printed to your terminal every 3 seconds. To see the Events which the Component Instance is now raising, you will need to use Lab.

Open Lab, load the MDB and connect to the Deployment Instance.

Open the 'Forwarded Events' view by clicking on the 'Communication' tab and ensuring 'Forwarded Events' is selected. You should see the Events arriving in the Forwarded Events table.

Once you are happy to proceed, disconnect from the Deployment Instance and terminate the process in the terminal.

You have successfully added a Task to the EventPublisher Component Implementation which prints log messages and Publishes Event Messages

There is currently no logic controlling when it Publishes an Event. In the next section you’ll add a Parameter to control this behaviour.

Controlling the publishEvent Task

In the previous tutorial you learned that Parameters are used to give insight into, or control over, a Component Instance's behaviour, and worked with a Read-Only Parameter.

It is also possible to define Mutable Parameters, whose values may be set externally to the Component Type. When implementing a Mutable Parameter you must provide a Parameter setter function as well as a Parameter getter.

In this section of the tutorial you will add a Mutable Parameter to the EventPublisher Component Type and update the publishEvent Task to control when Events are Published.

Adding the eventPeriod Parameter

Currently EventPublisher Publishes an Event every time its publishEvent Task runs.

You will now add a new eventPeriod Parameter so that the Component Implementation Publishes an Event 1 in every eventPeriod times the Task runs.

To add the new Parameter, add the following <Parameters> element to the Component Type's componentType.xml below the <!-- Parameters --> comment:

<Parameters>
  <Parameter name="eventPeriod">
    <Description>
      Controls how often the SomethingHappened event is published by the
      publishEvent task
    </Description>
    <Documentation><Markdown>
      A value of zero indicates the `SomethingHappened` event will never be
      published.

      A non-zero value indicates the `SomethingHappened` event will be published
      1 in every `eventPeriod` times the `publishEvent` task runs.

      - A value of '1' publishes the event *every* time the task runs
      - A value of '2' publishes the event *every second* time the task runs
      - A value of '3' publishes the event *every third* time the task runs
      - etc.
    </Markdown></Documentation>
    <Type>
      <Integer signed="false" bits="8"/>
    </Type>
  </Parameter>
</Parameters>

Notice that this Parameter does not specify a value for the readOnly attribute. It is not required for Mutable Parameters as its default value is "false".

Generate the Component Type.

In the template file you will now see EventPublisher_getEventPeriod() and EventPublisher_setEventPeriod() - the Parameter getter and setter functions.

Copy and paste these functions into EventPublisher.c.

Uncomment the gt_LockTimeout global variable at the top of EventPublisher.c. You will need it to implement the new Parameter getter and setter functions.

Show the change in EventPublisher.c
  /** The lock access timeout */
- // static const ShortTime_t gt_LockTimeout = EVENTPUBLISHER_CONFIG_LOCK_TIMEOUT;
+ static const ShortTime_t gt_LockTimeout = EVENTPUBLISHER_CONFIG_LOCK_TIMEOUT;

You will now use the same pattern as in the previous tutorial to store this Parameter's value.

Add a member to the EventPublisher_t Component Type Structure (defined in library/component_types/EventPublisher/include/EventPublisher.h) to hold the value of the eventPeriod Parameter.

Use a type and name consistent with the Parameter's definition in componentType.xml.

Show the new member added to EventPublisher_t
/** The EventPublisher component */
typedef struct
{
    /** A protection lock to protect the EventPublisher state */
    Task_ProtectionLock_t t_Lock;
-   /* TODO: Add component state variables here */
+   /** Data storage for the eventPeriod parameter */
+   uint8_t u8_EventPeriod;
}
EventPublisher_t;

Set u8_EventPeriod to 0 in EventPublisher_localInit(). This will ensure Component Instances do not Publish the SomethingHappened Event by default.

Show the initialisation added to EventPublisher_localInit()
status_t EventPublisher_localInit
(
    EventPublisher_t *pt_EventPublisher,
    const EventPublisher_Init_t *pt_InitData
)
{
+   /* By default, disable raising the SomethingHappened event */
+   pt_EventPublisher->u8_EventPeriod = 0;
+
    /* Initialise the protection lock */
    return Task_ProtectionLock_init(
        &pt_EventPublisher->t_Lock,
        EVENTPUBLISHER_CONTAINER_TASKING_TYPE(pt_EventPublisher));
}

You now need to update the getter and setter functions you copied into EventPublisher.c earlier so that they provide read and write access, respectively, to u8_EventPeriod.

As in the previous tutorial, these functions must only access the u8_EventPeriod member from within a locked section.

Update EventPublisher_getEventPeriod() so that it accesses u8_EventPeriod within a locked section.

Show the changes to the EventPublisher_getEventPeriod() implementation
/*---------------------------------------------------------------------------*
 * Get the eventPeriod parameter
 *---------------------------------------------------------------------------*/

status_t EventPublisher_getEventPeriod
(
    EventPublisher_t *pt_EventPublisher,
    uint8_t *pu8_Value
)
{
    status_t t_Status;          /* The current status */

    TASK_PROTECTED_START(&pt_EventPublisher->t_Lock, &gt_LockTimeout, t_Status)
    {
-       /* TODO: Implement eventPeriod get accessor */
-       t_Status = STATUS_NOT_IMPLEMENTED;
+       *pu8_Value = pt_EventPublisher->u8_EventPeriod;
    }
    TASK_PROTECTED_END(&pt_EventPublisher->t_Lock, &gt_LockTimeout, t_Status);

    return t_Status;
}

Update EventPublisher_setEventPeriod() so that it sets the value of u8_EventPeriod within a locked section.

Show the changes to the EventPublisher_setEventPeriod() implementation
/*---------------------------------------------------------------------------*
 * Set the eventPeriod parameter
 *---------------------------------------------------------------------------*/

status_t EventPublisher_setEventPeriod
(
    EventPublisher_t *pt_EventPublisher,
    uint8_t u8_Value
)
{
    status_t t_Status;          /* The current status */

    TASK_PROTECTED_START(&pt_EventPublisher->t_Lock, &gt_LockTimeout, t_Status)
    {
-       /* TODO: Implement eventPeriod set accessor */
-       t_Status = STATUS_NOT_IMPLEMENTED;
+       pt_EventPublisher->u8_EventPeriod = u8_Value;
    }
    TASK_PROTECTED_END(&pt_EventPublisher->t_Lock, &gt_LockTimeout, t_Status);

    return t_Status;
}

Although this Parameter doesn’t control the Event yet, you can still test the Parameter accessor functions at this stage.

To do so, carry out the following steps, which you should now be familiar with:

  • Build the Mission

  • Run the Deployment Instance

  • Open Lab

  • Load the MDB

  • Connect to the Deployment Instance

  • Add a command card for the eventPeriod Parameter to the My Commands section of the Commanding view.

  • Expand the eventPeriod command card.

Notice that the expanded command card contains both a Get ⬇ button and a Set ⬆ button. You can use these to get or set the value stored in u8_EventPeriod, respectively.

Give this a try and make sure that the Parameter value changes as expected.

When you are happy that it is working as expected, disconnect from, and terminate the Deployment Instance.

Now that you can set the value of u8_EventPeriod, you need to update the publishEvent Task to make use of it. You’ll do this in the next section.

Updating the publishEvent Task

As described in the eventPeriod <Documentation> element you added to the componentType.xml, your goal here is to Publish an Event every eventPeriod times the publishEvent Task runs.

To implement this functionality you will need a counter to keep track of how many times the publishEvent Task has run.

This counter will form part of an EventPublisher Component Instance's state, so as with u8_EventPeriod, you need to add it to the EventPublisher_t Component Type Structure.

Show the new member added to EventPublisher_t
/** The EventPublisher component */
typedef struct
{
    /** A protection lock to protect the EventPublisher state */
    Task_ProtectionLock_t t_Lock;
    /** Data storage for the eventPeriod parameter */
    uint8_t u8_EventPeriod;
+   /** Counter to track how many times publishEvent has run */
+   uint8_t u8_TaskCounter;
}
EventPublisher_t;

As with u8_EventPeriod, set this member to 0 in EventPublisher_localInit().

Show the initialisation added to EventPublisher_localInit()
status_t EventPublisher_localInit
(
    EventPublisher_t *pt_EventPublisher,
    const EventPublisher_Init_t *pt_InitData
)
{
    /* By default, disable raising the SomethingHappened event */
    pt_EventPublisher->u8_EventPeriod = 0;
+
+   /* Start counting how many times publishEvent has run from zero */
+   pt_EventPublisher->u8_TaskCounter = 0;

    /* Initialise the protection lock */
    return Task_ProtectionLock_init(
        &pt_EventPublisher->t_Lock,
        EVENTPUBLISHER_CONTAINER_TASKING_TYPE(pt_EventPublisher));
}

You’ll now need to implement logic in EventPublisher.c to make the behaviour of the eventPeriod parameter match its documentation.

There are several ways to do this. One way is to update the publishEvent Task so that each time it runs, u8_TaskCounter is incremented. When u8_TaskCounter reaches u8_EventPeriod, Publish the Event and reset the counter to 0.

Remember that to guarantee thread safety, you must use a locked section when accessing u8_EventPeriod, because it is now accessed from several different contexts.

Try and make the changes to publishEvent yourself if you wish, or, if you’d prefer, use the implementation we’ve provided below.

Show the updated implementation of the publishEvent task
/*---------------------------------------------------------------------------*
 * A task used to periodically publish events
 *---------------------------------------------------------------------------*/

void EventPublisher_taskPublishEvent
(
    EventPublisher_t *pt_EventPublisher
)
{
+   status_t t_Status;
+
    EVENTPUBLISHER_CONTAINER_LOG_INFO(
        pt_EventPublisher, "EventPublisher.publishEvent executed!");

-   EventPublisher_EDSPublisher_publishEventTask(
-       pt_EventPublisher,
-       EVENTPUBLISHER_EVENTID_SOMETHING_HAPPENED,
-       STATUS_SUCCESS,
-       NULL,
-       0);
+   TASK_PROTECTED_START(&pt_EventPublisher->t_Lock, &gt_LockTimeout, t_Status)
+   {
+       /* Only publish events if eventPeriod is non-zero */
+       if (pt_EventPublisher->u8_EventPeriod != 0)
+       {
+           pt_EventPublisher->u8_TaskCounter++;
+
+           /* Once we've counted up to eventPeriod, publish the event and reset
+            * the counter */
+           if (pt_EventPublisher->u8_TaskCounter >=
+               pt_EventPublisher->u8_EventPeriod)
+           {
+               EventPublisher_EDSPublisher_publishEventTask(
+                   pt_EventPublisher,
+                   EVENTPUBLISHER_EVENTID_SOMETHING_HAPPENED,
+                   STATUS_SUCCESS,
+                   &pt_EventPublisher->u8_EventPeriod,
+                   sizeof(pt_EventPublisher->u8_EventPeriod));
+
+               pt_EventPublisher->u8_TaskCounter = 0;
+           }
+       }
+   }
+   TASK_PROTECTED_END(&pt_EventPublisher->t_Lock, &gt_LockTimeout, t_Status);
}

Notice that we used the value of eventPeriod as the information field for the Published Event. As mentioned previously, this field allows you to provide extra information associated with your Event.

Build the Mission and run the Deployment Instance. Using Lab, confirm that your implementation functions correctly and meets the requirements laid out in the eventPeriod Parameter documentation.

Remember that the value is set to 0 during start up, so you won’t see any Events being Published until you set it to a non-zero value.

Wrap Up

In this tutorial you have:

  • Reinforced your understanding of how to work with Component Types and their implementations.

  • Learned how to use Periodic Tasks to make a Component Type do some work regularly and autonomously.

  • Been introduced to Services, specifically Publish-Subscribe Services.

  • Learned how to define Events, and how to Publish them from Component Types.

  • Learned how Parameters can be used to control the behaviour of Component Instances.

  • Used these concepts within a Component Implementation to meet some basic functional requirements. This approach of combining the features of Component Types to produce solutions is a key part of successful development with Flightkit.

What Now?

This concludes the basic Flightkit tutorials. These tutorials have given you the basic tools you need to produce operational flight software with Flightkit.

We have provided further tutorials which will help you learn how to use some more advanced features of Component Types. The first of these looks at details of using Services which were not covered in this tutorial.

Click here to move on to that now.