Skip to content

Target Manager Programming Discussion

Gary edited this page Mar 10, 2015 · 2 revisions

Table of Contents

The ATF Target Manager Sample uses TargetEnumerationService and related components to manage targets. Targets are created by applications and are network endpoints, such as IP addresses, PS3™ and PS4™ DevKits, and PlayStation®Vita DevKits.

Target providers, such as TcpIpTargetProvider, discover and report targets of their specific type and these targets' parameters. Once found, you can edit and, in some cases, add and remove, targets, of that type with the provider. TargetEnumerationService enumerates all available target providers, combining the targets' information into a list view for displaying and editing.

TargetManager has very little code of its own, and relies on interfaces and classes in the Sce.Atf.Applications.NetworkTargetServices namespace for its operation. However, TargetManager does not use all of the interfaces and classes in this namespace. This namespace also supports creating and servicing targets, which TargetManager does not do. This article focuses on the items used by TargetManager in this namespace.

Several applications based on ATF create their own target managers similar to the TargetManager sample. These applications include the StateMachine and Scream tools.

Programming Overview

TargetManager simply imports target related components to build the application. These components reside in the Sce.Atf.Applications.NetworkTargetServices namespace, which also contains an interface and classes that serve building targets.

Targets are classified by the type of the communication protocol used to communicate with the target. There are two protocols: TCP for x86, PS3™, and PS4™ targets, and Deci4p for PlayStation®Vita DevKits, and these describe the two types.

The classes and interfaces used can be divided into consumers and providers. Consumers use information from the providers, which compile a list of targets of each type. ITargetConsumer handles updating the target list user interface and provides a target list for the consumer. ITargetProvider enumerates targets for the particular provider and adds and removes targets.

The consumer is TargetEnumerationService, implementing ITargetConsumer. This component creates a ListView control for the targets and their information, embodied in the TargetInfo class.

The providers are TcpIpTargetProvider and Deci4pTargetProvider, both implementing ITargetProvider. These manage the list of targets: editing, and for TcpIpTargetProvider, adding, and removing them. TCP targets are added by users; Deci4p are discovered by the provider. These classes inform the user when changes occur so the target display list can be updated.

TargetCommands creates the context menu commands on the ListView control and is their command client.

Target Interfaces

There is one interface for target consumers, another for providers.

ITargetConsumer Interface

ITargetConsumer is for classes that consume network target information, such as TargetEnumerationService, which implements this interface. Such classes usually provide a user interface to view and edit targets. ITargetConsumer consists of the following:

  • TargetsChanged(): Updates changed targets for a given provider.
  • SelectedTargets: Gets or sets an enumeration of the selected targets in the consumer, if any, as an IEnumerable<TargetInfo>. The TargetInfo class describes target information. For details, see TargetInfo Class.
  • AllTargets: Gets all targets in the consumer as an IEnumerable<TargetInfo>

ITargetProvider Interface

ITargetProvider provides information about a particular kind of target available on the network, such as a TCP accessible device. Its methods and properties access the providers of this type:

  • Name: Get the provider name.
  • GetTargets(): Get an enumeration of the TargetInfo for all the targets.
  • CanCreateNew: Get whether a new target can be created.
  • CreateNew(): Create a new target, returning its TargetInfo.
  • AddTarget(): Add a new target with the given TargetInfo.
  • Remove(): Remove the target with the given TargetInfo.

Target Consumer Classes

TargetEnumerationService Component

TargetEnumerationService is a MEF component that does the following:

  • Imports the available target providers created by the host application.
  • Creates a ListView with target information.
  • Acts as control host client for this ListView control. For details, see Control Host Client.
  • Implements ITargetConsumer.

Importing Target Providers

TargetEnumerationService imports all components implementing ITargetProvider, so that it can display their information:

[ImportMany]
private IEnumerable<ITargetProvider> m_targetProviders= null;

The field m_targetProviders is get or set in the TargetProviders property.

Providers implemented in the ATF framework include the Deci4pTargetProvider and TcpIpTargetProvider components. More such providers could be added. For the discussion of target providers, see Target Provider Classes.

Creating ListView Control

The component's IInitializable.Initialize() method calls SetUpTargetsView() to create a targets display control, which can be used as a docked control or a stand alone dialog:

void IInitializable.Initialize()
{
    // force creation of the window handles on the GUI thread
    // see http://forums.msdn.microsoft.com/en-US/clr/thread/fa033425-0149-4b9a-9c8b-bcd2196d5471/
    var handle = MainForm.Handle;

    var control = SetUpTargetsView();

    if (!ShowAsDialog)
    {
        m_controlHostService.RegisterControl(
            control,
            new ControlInfo(
                "Targets".Localize(),
                "Controls for managing targets.".Localize(),
                StandardControlGroup.Bottom),
            this);
    }

    if (m_settingsService != null)
    {
        m_settingsService.RegisterSettings(this,
            new BoundPropertyDescriptor(this, () => PersistedUISettings, "UI Settings".Localize(), null, null));
        m_settingsService.RegisterSettings(this,
            new BoundPropertyDescriptor(this, () => PersistedSelectedTargets, "Selected Targets".Localize(), null, null));
    }

}

SetUpTargetsView() creates and configures a DataBoundListView, which extends ListView with data binding and cell editing functionality. If the control is not a dialog, it's registered with the Control Host Service. Settings in the control that persist between sessions are registered with the Settings Service. The properties PersistedUISettings and PersistedSelectedTargets get and set persistent values.

TargetEnumerationService also imports context menu providers for its context menu:

[ImportMany]
private IEnumerable<Lazy<IContextMenuCommandProvider>> m_contextMenuCommandProviders;

Menu items for the control are provided by the TargetCommands component, which implements IContextMenuCommandProvider. For details, see TargetCommands Component.

The bulk of the code in TargetEnumerationService creates the DataBoundListView control and handles its events. For example, the CellValidating event is raised when one of the cells of the DataBoundListView changes; the listView_CellValidating() event handler validates the change.

Control Host Client

This client is very simple. The only method that does anything is Activate(), which brings focus to the DataBoundListView control to interactively select and edit targets.

ITargetConsumer Implementation

The ITargetConsumer.TargetsChanged() method updates the target view in the DataBoundListView control. All target providers call TargetsChanged() when a target is added, removed, or changed so the target display stays current.

The properties SelectedTargets and AllTargets get known targets as an IEnumerable<TargetInfo>. SelectedTargets can also set the target selection.

Target Provider Classes

Target providers manage targets that are using a given type of communications protocol. Deci4p is a proprietary protocol handling the PlayStation®Vita DevKit. Everything else, x86, PS3™, and PS4™, is handled by TCP.

This section discusses the target provider components and their auxiliary classes.

TargetInfo Class

TargetInfo encapsulates target information in its gettable and settable properties, which are all string values except for Scope:

  • Name: Target name.
  • Platform: Type of platform the target can run on, such as as "Ps3" or "Vita".
  • Endpoint: Network endpoint in string format; an IP address for TCP.
  • Protocol: Type of protocol the target can use. Currently, this is either "Tcp" or "Deci4p".
  • Scope: TargetScope enumeration indicating how the target is persisted for applications that use TargetManager:
    • PerApp: Save target data for the current application. All users on this computer see this target when they run this application. Other applications that use TargetManager do not see this target. This is the default value for TCP targets.
    • PerUser: Save target data for the current user. Other applications that use the TargetManager can see this target for this user; other users won't see this target in any application.
    • AllUsers: Save the target data for all users and applications, so this target is visible for all users in any application that uses the TargetManager framework.
TargetInfo implements INotifyPropertyChanged, which contains one event, PropertyChanged. It has the method OnPropertyChanged() that simply raises this event.

TargetInfo has two ATF classes that derive from it, described in the next sections.

TcpIpTargetInfo Class

TcpIpTargetInfo describes TCP target information, and is used by TcpIpTargetProvider. TcpIpTargetInfo contains methods to verify that the IP address provided is valid. Its Validate() method checks this, as well as the other properties, and is called by TargetEnumerationService when any properties change in a TCP target listed.

TcpIpTargetInfo also has derived classes:

  • X86TargetInfo: X86 target information.
  • Ps3TargetInfo: PS3™ target information.
  • Ps4TargetInfo: PS4™ target information.
These class's constructors set some default values, as for Ps3TargetInfo:
public Ps3TargetInfo()
    : base()
{
    Name = "Ps3Host";
    Platform = PlatformName;
    Endpoint = "10.89.0.0:1338";
}

Deci4pTargetInfo Class

Deci4pTargetInfo holds information about a Deci4p target and is used by Deci4pTargetProvider in its process of finding active targets of this type.

TcpIpTargetProvider Component

TargetManager imports the TcpIpTargetProvider component, which is one of the two providers implementing ITargetProvider.

TCP targets are not found; they are added by users. The list of targets is persisted using the Setting Service to save the list between sessions. This requires using the Setting Service to set the saved values when a target changes and to read the saved values when the application starts. The PersistedTargets property gets and sets the persisted targets list.

The ITargetProvider implementation manipulates the target list. For instance, GetTargets() retrieves objects from the targets list in m_targets:

public IEnumerable<TargetInfo> GetTargets(ITargetConsumer targetConsumer)
{
    foreach (var target in m_targets)
        yield return target;
}

CreateNew() returns a new TcpIpTargetInfo:

public virtual TargetInfo CreateNew()
{
    var newTarget = new TcpIpTargetInfo();
    return newTarget;
}

AddTarget() adds the given target to the list and calls TargetsChanged() to inform each target consumer so its lists can be updated:

public bool AddTarget(TargetInfo target)
{
    if (target is TcpIpTargetInfo && !m_targets.Contains(target))
    {
        m_targets.Add(target);
        foreach (var targetConsumer in TargetConsumers)
            targetConsumer.TargetsChanged(this, m_targets);
        return true;
    }
    return false;
}

TcpIpTargetProvider imports as many ITargetConsumer implementers as it can find to populate its TargetConsumers property, which is used in AddTarget():

[ImportMany]
protected IEnumerable<ITargetConsumer> TargetConsumers { get; set; }

Finally, TcpIpTargetProvider has its own derived classes to match up with TcpIpTargetInfo's derived classes:

  • X86TargetProvider: X86 target provider.
  • Ps3TargetProvider: PS3™ target provider.
  • Ps4TargetProvider: PS4™ target provider.
These provider classes are simple. For example, Ps3TargetProvider has two gettable string properties and a CreateNew() method that constructs and returns a Ps3TargetInfo:
public class Ps3TargetProvider : TcpIpTargetProvider
{
    /// <summary>
    /// Gets the provider's user-readable name</summary>
    public override string Name { get { return "PS3 Target".Localize(); } }

    /// <summary>
    /// Gets the the identifier of the provider</summary>
    /// <returns>A string that contains the identifier.</returns>
    public override string Id { get { return @"Sce.Atf.Ps3TcpIpTargetProvider"; } }


    /// <summary>
    /// Creates a new target</summary>
    /// <remarks>Creates and returns a TargetInfo, but does not add it to the watched list</remarks>
    /// <returns>TargetInfo for new target</returns>
    public override TargetInfo CreateNew()
    {
        var newTarget = new Ps3TargetInfo();
        return newTarget;
    }
}

Deci4pTargetProvider Component

TargetManager also imports the Deci4pTargetProvider component, one of the two providers implementing ITargetProvider. This supports only the PlayStation®Vita platform.

Unlike TCP targets, you can't create targets — PlayStation®Vita targets can only be discovered. Its IInitializable.Initialize() method starts a background task in another thread to attempt to find these targets with its FindTargets() method, adding them to a list as they are found. FindTargets() only works if you have installed the PlayStation®Vita SDK on your local computer.

This limitation makes ITargetProvider's implementation rudimentary. Name always gets "Vita Target". CanCreateNew gets false. CreateNew() throws an exception. Both AddTarget() and Remove() do nothing but return false. GetTargets() does return the list of targets found.

Deci4pTargetProvider also imports as many ITargetConsumer implementers as it can find to update the target display of each consumer:

[ImportMany(typeof(ITargetConsumer))]
protected IEnumerable<ITargetConsumer> TargetConsumers { get; set; }

TargetCommands Component

TargetCommands adds the context menu commands for the targets list control, providing the command client for these commands, so it implements ICommandClient:

public class TargetCommands : ICommandClient, IContextMenuCommandProvider, IInitializable

TargetProviders Property

To create the right commands, TargetCommands needs to know which target providers are available. It does so by importing all exported ITargetProvider and accessing them with TargetProviders:

[ImportMany]
private IEnumerable<ITargetProvider> m_targetProviders = null;

/// <summary>
/// Gets or sets the target providers</summary>
public IEnumerable<ITargetProvider> TargetProviders
{
    get { return m_targetProviders; }
    set { m_targetProviders = value; }
}

Command Creation

TargetCommands's IInitializable.Initialize() creates the commands, based on available targets:

void IInitializable.Initialize()
{
    if (CommandService == null)
        return;

    if (Deci4pTargetProvider.SdkInstalled)
    {
        var cmdInfo = new CommandInfo(
            CommandTag.VitaNeighborhood,
            null,
            null,
            "Edit Vita Target in Neighborhood".Localize(),
            "Edit Vita Target in Neighborhood".Localize());
        cmdInfo.ShortcutsEditable = false;
        CommandService.RegisterCommand(cmdInfo, this);
    }

    foreach (var targetProvider in TargetProviders)
    {
        if (targetProvider.CanCreateNew)
        {
            string addCmdTag = AddNewString.Localize() + targetProvider.Name;
            CommandService.RegisterCommand(
                new CommandInfo(
                    addCmdTag,
                    null,
                    null,
                    addCmdTag,
                    "Creates a new target".Localize()),
                this);

            m_addTargetsCmdTags.Add(addCmdTag);


            string remCmdTag = "Remove ".Localize() + targetProvider.Name;
            CommandService.RegisterCommand(
                new CommandInfo(
                    remCmdTag,
                    null,
                    null,
                    remCmdTag,
                    "Remove selected target".Localize()),
                this);

            m_removeTargetsCmdTags.Add(remCmdTag);
        }
    }
}

Deci4p target providers are treated differently than TCP target providers. First, the method checks that a PlayStation®Vita SDK is installed with the Deci4pTargetProvider.SdkInstalled property. If present, it adds a command to edit PlayStation®Vita targets.

Next, Initialize() iterates through TargetProviders to create commands to add and remove targets for each provider. It first checks the CanCreateNew for the provider to make sure that new targets can be created; they can't for PlayStation®Vita, because Deci4pTargetProvider's property is this:

public bool CanCreateNew { get { return false; } }

As a result, Add and Remove commands are created only for TcpIpTargetProvider. The general loop is used, so other types of providers can be added easily.

IContextMenuCommandProvider Implementation

This interface provides a method to get commands that apply to a given context:

IEnumerable<object> IContextMenuCommandProvider.GetCommands(object context, object selectedTargets)
{
    m_selectedTargets = null;
    if (context.Is<ITargetConsumer>())
    {
        m_targetConsumer = context.Cast<ITargetConsumer>();
        m_selectedTargets = selectedTargets as IEnumerable<TargetInfo>;
        foreach (var cmdTag in m_addTargetsCmdTags)
            yield return cmdTag;

        if (m_selectedTargets != null && m_selectedTargets.Any())
            foreach (var cmdTag in m_removeTargetsCmdTags)
                yield return cmdTag;

        yield return CommandTag.VitaNeighborhood;
    }
}

The commands are available only for the target list control provided by TargetEnumerationService, the only implementer of ITargetConsumer, so the method first checks that the given context can be adapted to ITargetConsumer. If so, it proceeds and saves this context adapted to ITargetConsumer in m_targetConsumer, where it is used later to test whether a command can be performed.

Next, the selected objects are adapted to IEnumerable<TargetInfo> and saved in the field m_selectedTargets, which is used to test whether commands can be performed and to perform commands. Targets are treated as TargetInfo objects in TargetManager, so this adaptation works.

Finally, GetCommands() iterates through the lists m_addTargetsCmdTags and m_removeTargetsCmdTags of commands created in Initialize(). Remove commands are only returned when there are targets selected, hence the m_selectedTargets \!= null && m_selectedTargets.Any() test.

Command Client

The ICommandClient implementation uses the TargetProviders property and field values like m_addTargetsCmdTags, already set up by Initialize() and IContextMenuCommandProvider.GetCommands(). For example, here's DoCommand():

void ICommandClient.DoCommand(object commandTag)
{
    if (CommandTag.VitaNeighborhood.Equals(commandTag))
    {
        // Invoke "Edit Vita Target in Neighborhood" command merely launches the PSP2 Neighborhood app that comes with Vita SDK installer,
        // as if you double click "Neighborhood for PlayStation(R)Vita" on your desktop icon.
        // This is intended just for a convenience helper to allow users directly bring up the PSP2 app without a detour to desktop first,
        // as PSP2 Neighborhood app can reboot, power off, and do much more for the Vita kit.

        // {BA414141-28C6-7F3C-45FF-14C28C11EE88} is the registered Neighborhood for PlayStation(R)Vita Shell extension
        System.Diagnostics.Process.Start("Explorer.exe", @"/e,/root,::{BA414141-28C6-7F3C-45FF-14C28C11EE88}");
    }
    else if (m_addTargetsCmdTags.Contains(commandTag))
    {
        foreach (var targetProvider in TargetProviders)
        {
            string addCmdTag = AddNewString.Localize() + targetProvider.Name;
            if (addCmdTag.Equals(commandTag))
            {
                targetProvider.AddTarget(targetProvider.CreateNew());
                break;
            }
        }
    }
    else if (m_removeTargetsCmdTags.Contains(commandTag))
    {

        foreach (var item in m_selectedTargets)
            foreach (var provider in TargetProviders)
                provider.Remove(item);
    }
}

The commandTag parameter is matched against existing commands in the command lists, and if a match is found, the corresponding command is executed. The Add command logic does additional processing to find the right target provider for the command and uses the target provider's AddTarget() method to add the target. The Remove processing tries to remove each selected target with each target provider's Remove() method, relying on it to do the right thing. Deci4pTargetProvider.Remove() does nothing — PlayStation®Vita targets can't be added or removed, only discovered — so that's not a problem. And TcpIpTargetProvider.Remove() checks that the target is one of the TCP targets, so that works also.

Topics in this section

Clone this wiki locally