wilke

In order to understand a specific aspect of SharePoint it is sometimes useful to be able to peek in the databases. One such aspect where the backend storage is important is the usage of content types. If a content type is assigned to a list this information is written down in the content database. Depending on how the association is performed the actual data that is stored changes, the association can be performed based on features or one can use the UI to make the association.

The field where this content type association is stored is ‘tp_ContentTypes’ in the table ‘AllLists’. In SharePoint 2007 this field contained literal xml fragments, in SharePoint 2010 the field contains compressed data, so it is not immediately readable.

The data type of the ‘tp_ContentTypes’ field is tCompressedString which is actually a User Defined Type definition pointing to a varbinary(max) data type. The compression method of these fields are described in this document: MS-WSSF02 (File Operations Database Communications Version 2 Protocol Specification).

If you go to topic 2.2.5.8 (WSS Compressed Structures) in this document you can see that the zlib compression technique is used to compress the data. From the structure schema you can also see that the offset of the compressed data is 12 bytes. Here starts the zlib compressed data. To my limited understanding zlib is some sort of envelope specification with support for different compression mechanisms, the one that is used most though is the deflate compression technique. The .NET framework has support for this compression technique via the ‘DeflateStream’ class. I have read in some articles that there are more robust ways to work with the deflate compression but for our purposes it will do. In order to use the method ‘CopyTo’ I compiled the code for the .NET 4.0 framework, it makes reading from the underlying stream much easier.

Following code snippet shows how you can decompress the data coming from the ‘tp_ContentTypes’ field:

private string Decompress(byte[] compressedBytesBuffer)
{
  string uncompressedString = String.Empty;
  using (MemoryStream compressedMemoryStream = new MemoryStream(compressedBytesBuffer))
  {
    compressedMemoryStream.Position += 12; // Compress Structure Header according to [MS -WSSFO2].
    compressedMemoryStream.Position += 2; // Zlib header.

     using (DeflateStream deflateStream = new DeflateStream(compressedMemoryStream, CompressionMode.Decompress))
      {
        using (MemoryStream uncompressedMemoryStream = new MemoryStream())
        {
          deflateStream.CopyTo(uncompressedMemoryStream);

         uncompressedMemoryStream.Position = 0;

         using (StreamReader streamReader = new StreamReader(uncompressedMemoryStream))
        {
            uncompressedString = streamReader.ReadToEnd();
          }
      }
     }
  }
  return uncompressedString;
}

The method itself accepts an array of bytes, in order to convert the string representation of the byte sequence (coming from the textbox) there is a helper function that returns a corresponding byte sequence. This means that the helper method converts for example the string “A8B2” to a byte sequence {0xA8, 0xB2}.

I have created a small windows forms application that performs this decompression. The interface is very basic as I put together this small utility rather quickly.

SNAGHTML79bcbb

The source code for this small utility can be found here. The code is written in C# using Visual Studio 2010.

This is part two of a two part article. In the first part I reported on a couple of findings when working with the SPWebConfigModification API, in the this part I will introduce a simulator tool that allows to learn, experiment and generate code for SPWebConfigModification related tasks.

The utility can be downloaded from codeplex, it can be found here: http://wcms.codeplex.com.

This utility is applicable for SharePoint 2007 and SharePoint 2010. It has been compiled with a reference to the SharePoint 2010 assembly (hence the .NET 3.5 framework requirement), by changing the SharePoint version in the options screen a binding redirect is added in the configuration file so we can make it work with SharePoint 2007. Changing this setting requires a restart of the application.

WebConfigModificationSimulator – Some Features

  • Work with SPWebConfigModification without changing the configuration database or Web.Config – (avoid corrupt property bags, duplicate entries, …)
  • Work with actual artifacts (SharePoint Web.config, SPWebConfigFileChanges property bag entries) – (needs SharePoint Permissions, read-only)
  • Work on empty xml (or xml of own choice) to have clearer view.
  • Drag and Drop of entries between the two entry lists.
  • Auto lookup and complete when entering modification paths into the Web.config (avoids mistakes or typos).
  • Wizard like entry form that allows to easily create SPWebConfigModification entries, the system composes the name and value properties.
  • Code generation with possibility to save either in VB.NET or C#.
  • Save list entries to disk for later use.

WebConfigModificationSimulator – Introduction

This utility lets you experiment with SPWebConfigModification without the risk of messing up the content configuration database or the Web.config file. The main UI looks like this:

MainUI

When the window is loaded the utility tries to get a list of SPWebApplication instances in the local farm. If the user has not the necessary privileges to perform this action the text part of the web.config dropdown will show the message “Error – Could not load SharePoint WebApplications.“. The utility is fully functional but no live information can be retrieved from SharePoint. In this case you can add entries yourself (and maybe save them to file for later retrieval).

Adding SPWebConfigModification entries is done in the left part of the window, when these modifications are ‘applied’ the config file on the right will be updated. You can see that there are two lists where you can enter new SPWebConfigModification entries. This is to simulate the entries stored in the SPWebConfigFileChanges and SPWebApplication property bag (hence the Database icon) and the ones added or removed by code. You do not have to use the first list to experiment with SPWebConfigModification entries, but using the two will provide a more realistic view on what SharePoint is doing behind the scenes.

On the right part of the window you can see a Code tab, this is where you can generate code for the SPWebConfigModification entries in the two lists, this will be explained further in this article.

The entry area below the second list allows you to add new entries or modify existing ones. The name and value properties have to be added as they are written to the SharePoint system.

The utility provides a way to abstract this by using a kind of wizard entry form. This makes it really easy to add new SPWebConfigModification entries, no guessing anymore what needs to be entered. This will be shown in the next topic.

This code works exactly as the code SharePoint executes behind the scenes. I used reflector to extract the relevant parts, I mocked up some internal classes and the whole is an exact simulation of what happens when you work with SPWebConfigModification entries.

WebConfigModificationSimulator – Add SPWebConfigModification

Let’s do a first test with the simulator. We want to start simple and add a new node to the Web.config. Let’s call the node myFeatureConfig. It will be a node under our control, no one else should add nodes to it outside the scope of the feature itself so it is going to be an EnsureChildNode entry.

Start by clicking on the green plus icon (Add) in the header of the second list (or use the context menu on the second list), a new window will open:

AddEntry

The default Modification type is set to EnsureChildNode. We leave it as it is.

I already started typing in the Modification path textbox and you can see that I implemented auto lookup and auto complete. Since the node I want to add is directly under the configuration node I can just press Tab.

In the Node name textbox I type myFeatureConfig.

I then have the option to enter one or more attributes. Let’s just add one for fun, call it level and give it a value of 5.

Optionally you can enter then an xml fragment that is the innerXml of the new node. We do not have a need for that now so let’s leave it empty. The window should look now like:

AddEntry1

Notice that the name and value textbox contain automatically the correct value. In this way we are sure that these values are in sync.

Click OK.

This is the window as it will look like:

MainUI1

Now simulate ApplyWebConfigModifications of by clicking on the Apply Modifications icon (Apply). now the window looks like this:

MainUI2

The SPWebConfigModification entry has been applied and can be seen in the config file on the right. The entry is also shown in the first list now (which simulates that it has been stored in the SPWebConfigFileChanges and SPWebApplication property bags.

Remark:

A small note on the use of the checkboxes.

What I am trying to do here is simulate the process that is performed by custom code on one side (adding/removing SPWebConfigModification instances) and by SharePoint (the ApplyWebConfigModifications internal code). I gave it some thought but did not find an easy visual metaphor to represent this whole process. So enter the checkboxes…

The checkbox on the entries in the first list indicate whether we use either only the entry from the SPWebConfigFileChanges property bag (unchecked) or both from the SPWebConfigFileChanges and SPWebApplication property bags (checked). I use this to simulate the removal of SPWebConfigModification entries via code. Unchecking the checkbox in the previous scenario would be equal to:

SPWebService webService = SPWebService.ContentService;
SPWebApplication webApplication = webService.WebApplications[“SharePoint – 80”];
Collection<SPWebConfigModification> webConfigModifications = webApplication.WebConfigModifications;

webConfigModifications.Remove(webConfigModifications[0]);

///……

Once an entry exist in the first list a link becomes available in the first item of the second list (details). If you click on this link a new window will open that details what will be executed during the simulation, suppose we click on it when the first item in the first list is checked, we will see the following:

Details

This window shows indeed that the selected node will be first removed (as entry in the SPWebConfigFileChanges property bag) and then re-added as entry in the SPWebApplication property bag)

If the entry would be unchecked we would see:

Details2

The checkbox on the entries in the second list indicate only whether we ignore the selected entry from the ApplyWebConfigModifications process.

The utility has following context menu on the two lists:

ContextMenu

  • Get from ‘xxxx’: this menu item tries to retrieve the entries of the SPWebConfigFileChanges property bag. If the user has no access this option is not available. This menu item is only available on the first list.
  • Add WebConfigModification: shows the wizard entry form.
  • Delete WebConfigModification: deletes the currently selected entry from the list.
  • Load WebConfigModifications: loads the entries from a file into the selected list.
  • Save WebConfigModifications: saves the entries from the selected list to a file.
  • Apply WebConfigModifications: simulates the ApplyWebConfigModifications.
  • Clear All: removes all the entries from the selected list.

WebConfigModificationSimulator – Remove SPWebConfigModification

Now let’s continue with the previous scenario and simulate the removal of the SPWebConfigModification. When you remove a SPWebConfigModification entry through code you actually change the WebConfigModifications collection from either an SPWebApplication or SPWebService instance after which you call Update. This updates modifies actually the property bags in the configuration database. When you then call ApplyWebConfigModifications SharePoint uses these property bags to initiate the process.

This is what you can simulate by checking or unchecking the checkboxes in the first list. If you want to simulate the removal of a SPWebConfigModification entry all you have to do is uncheck the checkbox of that entry in the first list (remember this list represents the property bags in the configuration database)

Let’s do this in our example, uncheck the checkbox in the first list and either uncheck the checkbox in the second list or remove the entry by deleting it (if you leave this entry you simulate the addition of a new SPWebConfigModification entry and this is not what we try to do). This is what you should have now:

MainUI3

As you can see the entry has been removed from the Web.config file.

InfoThis utility allows you to verify whether the SPWebConfigModification properties are balanced in such a way that adding and removing nodes works correctly without having to modify a live environment.

WebConfigModificationSimulator – Code generation

The utility also allows to generate code for the entries currently defined. There is a choice between VB.NET and C#. Go to the Code tab, select you programming language and click on Generate. The utility will generate code using a preprocessed T4 template (new feature in Visual Studio 2010).

MainUI4

Here you see some auto generated code in C# for adding and removing SPWebConfigModification entries:

using System;
using System.Collections.ObjectModel;
using Microsoft.SharePoint.Administration;

namespace Wilke.SharePoint.Tools
{
public static class WebConfigModificationsHelper
{
private static SPWebService contentService = null;
private static SPWebApplication webApplication = null;

public static bool ApplyWebConfigModifications(string webAppUri)
{
bool result = false;

try
{
contentService = SPWebService.ContentService;
webApplication = SPWebApplication.Lookup(new Uri(webAppUri));

AddWebConfigModifications();
}
catch (Exception exception)
{
//TODO
//Provide appropriate exception handling.
}

return result;
}

private static bool AddWebConfigModifications()
{
bool result = false;

try
{
SPWebConfigModification spWebConfigModification = null;
spWebConfigModification = new SPWebConfigModification();
spWebConfigModification.Owner = “[WebConfigModificationTester]”;
spWebConfigModification.Path = “configuration”;
spWebConfigModification.Name = “myFeatureConfiguration[@level=’5′]”;
spWebConfigModification.Value = “<myFeatureConfiguration level=’5’/>”;
spWebConfigModification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
spWebConfigModification.Sequence = 0;
webApplication.WebConfigModifications.Add(spWebConfigModification);

spWebConfigModification = new SPWebConfigModification();
spWebConfigModification.Owner = “[WebConfigModificationTester]”;
spWebConfigModification.Path = “configuration/myFeatureConfiguration[@level=’5′]”;
spWebConfigModification.Name = “useDatabase”;
spWebConfigModification.Value = “<useDatabase/>”;
spWebConfigModification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
spWebConfigModification.Sequence = 0;
webApplication.WebConfigModifications.Add(spWebConfigModification);

webApplication.Update();
contentService.ApplyWebConfigModifications();
}
catch
{
throw;
}

return result;
}

private static bool RemoveWebConfigModificationsInternal(string owner, string path, string name)
{
bool result = false;
bool needsUpdate = false;

try
{
Collection<SPWebConfigModification> webConfigModifications = webApplication.WebConfigModifications;

int modificationCount = webConfigModifications.Count;
for (int index = modificationCount – 1; index > -1; index–)
{
if (webConfigModifications[index].Owner == owner &&
webConfigModifications[index].Path == path &&
webConfigModifications[index].Name == name)
{
webConfigModifications.Remove(webConfigModifications[index]);
needsUpdate = true;
}
}
if (needsUpdate)
{
webApplication.Update();
contentService.ApplyWebConfigModifications();
}
}
catch
{
throw;
}

return result;
}

private static bool RemoveWebConfigModificationsInternal(string owner)
{
bool result = false;
bool needsUpdate = false;

try
{
Collection<SPWebConfigModification> webConfigModifications = webApplication.WebConfigModifications;

int modificationCount = webConfigModifications.Count;
for (int index = modificationCount – 1; index > -1; index–)
{
if (webConfigModifications[index].Owner == owner)
{
webConfigModifications.Remove(webConfigModifications[index]);
needsUpdate = true;
}
}
if (needsUpdate)
{
webApplication.Update();
contentService.ApplyWebConfigModifications();
}
}
catch
{
throw;
}

return result;
}
}
}

Remarks:

  • Namespace and SPWebConfigModification owner can be changed via the options.
  • In this first version the T4 template is already preprocessed, in a future version I might make it as a configurable feature.

WebConfigModificationSimulator – Options

Options

  • SharePoint version: The version of the Microsoft.SharePoint assembly to use, changing this requires a restart of the application.
  • Path to SharePoint config files: the path where SharePoint stores its configuration files. Normally this inside the wss folder in the C:inetpub folder but this can be also in a different location.
  • Namespace for code generation: the namespace used when the code is generated.
  • Default modification owner: the owner set when creating SPWebConfigModification instances.
  • Frequently used node names: a list of node names frequently used in config files. This list is used for auto lookup in the node name textbox.
  • Nodes to ignore in auto lookup: SharePoint config files contain lots of entries that are not suitable for serving as parent node (e.g. SafeControls entries). During the collection of nodes to present in the auto lookup these nodes will not be included.
  • Auto lookup differentiating attributes: Some parent nodes have one or more attributes, when the node is selected as path we need to be able to uniquely identify this node. For this we use a differentiating attribute. This list represent attribute names that are frequently used to differentiate parent nodes with the same tag name.

WebConfigModificationSimulator – Known limitations

The auto lookup feature tries to enumerate the available nodes in the current config file (the one in the right part of the window). In order to identify nodes that contain attributes uniquely the utility uses following approach:

  • If the attribute has one of the following names: name, path or id, it will be used as differentiator of the node.
  • If the condition above is not met the first attribute is selected as differentiator.

This means that it is possible that a node with attributes is not found in the dropdown. In that case you can just type the path in the textbox.The options screen allows you to add additional attribute names tat should be recognized during the collection of the nodes to add for auto lookup.

WebConfigModificationSimulator – Acknowledgements

The code generation feature makes use of the ColorCode.dll. This library provides syntax coloring for several programming languages. The codeplex project can be found here: http://colorcode.codeplex.com/.

In order to keep a single executable package I used ILMERGE to merge the primary executable with the ColorCode.dll into one new merged executable. Using ILMERGE-GUI did help me not having to remember the command line arguments for ILMERGE. The codeplex project can be found here: http://ilmergegui.codeplex.com/.

WebConfigModificationSimulator – Feedback

This is the first time I publish a utility for a broader audience so I’m still in the process of fine tuning this process. If you have some constructive feedback please leave me a message, this might be about the articles on the utility or the utility itself.

In this post I will talk about an extension I added using the .NET DroneController. The extension itself is based on the new WorkflowDesigner control available in the Workflow Foundation 4.

In order to see a working example download the either Wilke.Interactive.Drone.Simple or Wilke.Interactive.Drone.PlayGround from codeplex.

It is now much easier to re-host the WorkflowDesigner and allow users to compose workflows in a custom application. This is the feature I want to apply in the Path Designer. You can find much more on re-hosting the WorkflowDesigner on the web, a starting place is here.

In order to provide a design experience to the user I created custom activities with corresponding activity designers. Using these activities a user can now compose a new workflow that will the ARDrone fly according to predefined commands.

The PathDesigner van be shown as a modal window as follows:

if (droneController != null && droneController.ConnectionStatus == ConnectionStatus.Open)
{
inputProvider = new AssistInputProvider(droneController);
inputProvider.Initialize(droneController.RollThrottleValue, droneController.PitchThrottleValue, droneController.HeightThrottleValue, droneController.YawThrottleValue);

PathDesigner pathDesigner = new PathDesigner(droneController);
pathDesigner.ShowDialog();

//Switch back to the selected inputprovider
inputProvider = new WiimoteInputProvider(droneController);
inputProvider.Initialize(droneController.RollThrottleValue, droneController.PitchThrottleValue, droneController.HeightThrottleValue, droneController.YawThrottleValue);
}
else
{
MessageBox.Show("This feature is only available if a connection is made with the ARDrone.");
}

From the code above you can see the following:

  • The PathDesigner needs an active instance of an object that implements the IDroneCommander interface (e.g. an active DroneController instance)
  • When working with the path designer I create an instance of the AssistInputProvider. This is a variation of the WiimoteInputProvider, the difference is that there is no accelerometer feedback. I use this input provider to make adjustments during the automated flight and in case of emergency I can land the ARDrone without having to run to the laptop.

This is how the UI looks like:

qiaywbow

On the left you see the activities that are currently supported. The middle panel shows the design surface. It is here that you can drag and drop activities to compose your workflow. Once the workflow has been created you can save it for later reuse. Clicking on the run button will start the workflow and pass the commands to the ARDrone.

There is also a possibility to activate workflow tracking. When activated the workflow engine will provide visual feedback on the current activity with a yellow border. There is still an issue with tracking in case of parallel activities. I will talk a bit more on this in another post were I will go into a bit more detail on some programming aspects of the DroneController and its related projects.

Here is a small video that shows the basic process:

Small demo on using the PathDesigner with the .NET DroneController

Remark:

The PathDesigner is experimental, I only implemented just enough to demonstrate the concept. Since I do not have that much space in my living room I was not able to play with it and design more advanced paths. Let me know if you like the concept.

In the last post I will take up some aspects of the DroneController and its related projects in somewhat deeper detail.

The binaries for this application can be found at http://dronecontroller.codeplex.com/releases. This application uses some new functionalities of WPF 4.0.

The interface of the application looks as follows:

nnpoejb2

The application uses a DroneController instance that connects to an ARDrone with networkid ‘ardrone_229’. I f you would like to get this application up and running make sure you replace this value in the code where the connection with the ARDrone is made.

Note: This image is taken in evening light conditions in my living room. It also gives a slight idea of the available surface I had working most of the time with the ARDrone. In the process I ruined at least two of my plants that are located in the corner of the living room.

As I already indicated the application is designed for a wide screen multi touch screen (1920×1080). On the right side of the screen you can see two controls that allow to pilot the ARDrone by multi-touch gestures on the screen. The radar allows you to control roll and pitch, it also supports single finger rotation which results in changing the yaw parameter of the ARDrone. The joystick on the left of the radar allows you to control height and yaw of the ARDrone. The button with the cog on top of the joystick allows to switch the joystick with two slider controls that can also be manipulated with gestures.

h4glbaex

The UI also lets you change the device you use to control the ARDrone:

image

Here you see the drop down menu that allows you to change the input provider. The Sensor Board is a circuit board that contains several sensors (accelerometer, touch, ambient light) that can be used to demonstrate the new Sensor capabilities available on Windows 7. I f you do not own such a circuit board this inputprovider can of course not be used. This is a link to the product page of the Flexis JM Badge Board.

For the design I based myself on some resources I found on the web:

Remark: The name of this application is PlayGround, and that is what it is. I am not a professional designer (although I would like to be somewhat more proficient on this), so this application is just to experiment with a couple of things. Just as I learned a lot from looking around on the web I am sure this application also has something to offer on this level. I always am eager to learn so if you find things that can be considered an ‘insult’ to professional coding please let me know.

In the following post I will talk about the Path Designer. This is an experimental extension that uses a re-hosted workflow designer (Windows Workflow Foundation 4).

In this post I will talk in a bit more detail how to create a new application that uses the .NET DroneController. The application I create will be a WPF application mostly because WPF has native support for WritebleBitmap. If we would use a windows forms application we would need to find a way to bind the WritebleBitmap to a PictureBox control. I did not yet spend time doing this.

When creating a new application there are basically three different phases to take care off:

  • Implement a method that creates a new DroneController instance, configures it and connects it to the ARDrone.
  • Implement methods that pass commands via the DroneController instance to the ARDrone.
  • Implement event handlers that allow to work with data coming from the ARDrone.

Let’s go over each of these phases in more detail:

Create a new DroneController instance:

When you create a new instance of the DroneController class you pass an instance of a DroneControllerConfiguration class. This can be done explicitly or implicitly.

Suppose I want to create a new instance of the DroneController using all the defaults I can do just:

DroneController droneController = new DroneController();

In this case the DroneController instance will be configured using the defaults of the DroneControllerConfiguration class:

/// <summary>
/// Initializes a new instance of the <see cref=”DroneControllerConfiguration”/> class.
/// </summary>
public DroneControllerConfiguration()
{
this.NetworkIdentifier = “ardrone_229”;
this.DroneIpAddress = “192.168.1.1”;
this.GuestIpAddress = “192.168.1.2”;
this.NavigationDataPort = 5554;
this.VideoStreamPort = 5555;
this.CommandPort = 5556;
this.ControlInfoPort = 5559;
this.EnableATCommandThread = true;
this.EnableNavigationDataThread = true;
this.EnableVideoStreamThread = true;
this.EnableControlInfoThread = false;
this.EnableInputFeedback = false;

this.RollThrottle = Constants.DefaultRollThrottleValue;
this.PitchThrottle = Constants.DefaultPitchThrottleValue;
this.HeightThrottle = Constants.DefaultHeightThrottleValue;
this.YawThrottle = Constants.DefaultYawThrottleValue;

DroneInfoTimerInterval = Constants.DroneInfoTimerInterval;
}

Here you see that the network identifier of the adhoc network created by the ARDrone is called ‘ardrone_229’. Yours will most probably be different, in this case you can change the default constructor of the DroneControllerConfiguration class.

Another way would be to construct a DroneController instance as follows:

DroneController droneController = new DroneController(“[your network identifier]”);

Here the DroneController instance will be created with all default values but

If you want to have more control you can create first an instance of a DroneControllerConfiguration class and pass this on when you create a new instance of the DroneController.

droneControllerConfiguration = new DroneControllerConfiguration();

droneControllerConfiguration.NetworkIdentifier = “ardrone_229”;
droneControllerConfiguration.EnableNavigationDataThread = true;
droneControllerConfiguration.EnableVideoStreamThread = true;
droneControllerConfiguration.EnableATCommandThread = true;
droneControllerConfiguration.EnableControlInfoThread = false;
droneControllerConfiguration.VideoFilePath = videosPath;
droneControllerConfiguration.PictureFilePath = picturesPath;
droneControllerConfiguration.EnableATCommandSimulation = simulate;
droneControllerConfiguration.EnableInputFeedback = true;

#region Throttle Values

droneControllerConfiguration.RollThrottle = .2f;
droneControllerConfiguration.PitchThrottle = .2f;
droneControllerConfiguration.YawThrottle = .6f;
droneControllerConfiguration.HeightThrottle = .4f;

#endregion

droneController = new DroneController(droneControllerConfiguration);

Remark:

I included the concept of throttle values because not everyone has a large empty warehouse at their disposition, in my case I only had +/- 2 m2 in my living room to spare. By playing with the throttle values you can minimize the effect of your input values on the ARDrone movement so that you can use and experiment with it in smaller places. Although this is also possible by changing some parameters on the ARDrone itself I decided to provide more accessible support with these throttle parameters on the DroneControllerConfiguration instance.

In order to be able to monitor the DroneController we can subscribe to the different events that this class exposes:

droneController.TraceNotificationLevel = TraceNotificationLevel.Verbose;
droneController.OnNotifyTraceMessage += new EventHandler<TraceNotificationEventArgs>(droneController_OnNotifyTraceMessage);
droneController.OnNotifyVideoMessage += new EventHandler<VideoNotificationEventArgs>(droneController_OnNotifyVideoMessage);
droneController.OnNotifyDroneInfoMessage += new EventHandler<DroneInfoNotificationEventArgs>(droneController_OnNotifyDroneInfoMessage);
droneController.OnConnectionStatusChanged += new EventHandler<ConnectionStatusChangedEventArgs>(droneController_OnConnectionStatusChanged);

Now we can connect to the ARDrone:

droneController.Connect();

Pass commands to the ARDrone

After you have created an instance of a DroneController class, it knows how to communicate with the ARDrone, you only have to tell it what commands it should send. This can be done in two ways:

Commands send directly to the DroneController instance

e.g.

if (droneController != null && droneController.ConnectionStatus == ConnectionStatus.Open)
{
droneController.SetFlatTrim();
droneController.StartEngines();
}
Commands send via an InputProvider

e.g.

if (droneController != null && droneController.ConnectionStatus == ConnectionStatus.Open)
{
inputProvider = new WiimoteInputProvider(droneController);
inputProvider.Initialize(droneController.RollThrottleValue, droneController.PitchThrottleValue, droneController.HeightThrottleValue, droneController.YawThrottleValue);
}

In this case no explicit commands are send to the DroneController instance. The object that implements the IInputProvider interface consumes the methods exposed by the IDroneCommander interface.

Implement the DroneController event handlers

The DroneController class exposes several events that provide feedback on different aspects. It is not mandatory to subscribe to these events but it makes working with the DroneController a lot easier.

These are the different events that are exposed:

  • OnNotifyTraceMessage – This event is used for general diagnostic messages. The TraceNotificationLevel enumeration can be used to throttle the level of detail.
  • OnNotifyVideoMessage – This event conveys the current video frame captured by the ARDrone cameras. The composition of the video frame depends on the selected video channel.
  • OnNotifyDroneInfoMessage – This event provides feedback on several aspects of the ARDrone, e.g. Battery Level. The existing code can be adapted if more information is needed.
  • OnConnectionStatusChanged – This event provides connection status feedback.
  • OnNotifyInputMessage – This event provides feedback on changes in flight parameters. This can be used to provide visual feedback on screen.
  • OnVisionDetectMessage – This event is triggered when tag detection is activated, it will tell whether a tag is detected an provide information on the detected tag.

Do not forget that the event handlers are not activated on the UI thread, if you need to access controls from within the event handler you need to go via the dispatcher (look in the sample source code how to do this if you are not familiar with this concept).

In the next post I will talk a small bit about the more advanced multi-touch WPF interface.

In this post I will talk about how to use the .NET DroneController with the ARDrone. The DroneController is created with Visual Studio 2010 and targets the .NET 3.5 framework. The main reason I target this framework is because the DroneController uses the updated WritebleBitmap class, this class exposes a BackBuffer property which allows modifying the image while is currently rendered and avoid smearing or tearing.

All code has been developed on a Windows 7 platform, using the code on Windows XP or Vista should just be fine (the only thing I am not sure about is the video encoding part, I have no clue what default functionality is available on these additional platforms). In case of issues on these part I can always take a look.

Running simple WPF client

Download the latest binaries from http://dronecontroller.codeplex.com/releases and extract to your hard disk.

Before you connect to the ARDrone make sure

  • The batteries are connected and all the green led’s are activated.
  • Your WI-FI adapter has a fixed IP address of 192.168.1.2 (this is the default address given by the ARDrone DHCP, since there is an issue on Windows platform with the ARDrone DHCP server behavior we make it a fixed address in stead of a dynamic)
  • You have connected to the adhoc network created by the ARDrone

If all goes well you should see following window:

00alzxq3

Change the Network Id with the name of the adhoc network created by your ARDrone.

Remark: Recording video and taking pictures relies on the existence of a C:Temp directory. Either make sure this directory exists or change the source code.

As you can see the interface is really simple. There are a couple of buttons and sliders to control the ARDrone. There are also two output areas, one for showing the video and another one for showing trace messages.

If you see messages like Nav:No activity on worker thread for 500 milliseconds. it means that the thread servicing the Navigation Data has timed out. Just disconnect and reconnect again.

Remark

The flight parameters you send to the ARDrone to change roll, pith, height or yaw have a value between –1 and 1. This simple interface has limited these parameters between –.1 and .1 in order to avoid making too brisk maneuvers. You can always adapt these settings if you have enough room.

In the next post I will discuss a bit more the ins and outs of creating a consumer for the .NET DroneController.

Finally I found some time to post my experiences with implementing a library in native C# that allows to pilot the ARDrone. In stead of explaining what the ARDrone exactly is I like just to point to the product site itself which can be found at: http://ardrone.parrot.com/parrot-ar-drone/usa/.

It basically is a programmable remotely controlled quadricopter. Communication happens via Wi-Fi. The out of the box API support focuses on development on iPhone, although it is possible to take the existing C SDK and compile it for use on other platforms. You can find plenty of examples when you look on the web. I took a slightly different approach in that I did not use the existing SDK (except for research on how it all works) but created a completely managed C# library from scratch.

This is the first post of the following series of articles:

The purpose of this article is not to go into much detail on the actual code, this is an effort I will maybe take up gradually at a later time, but to stay pragmatic and explain how the library can be used in real life.

This is a picture that shows the high level composition of the .NET DroneController:

image

The important parts of the DroneController library are:

DroneControllerConfiguration: When a new DroneController is created you can use the DroneControllerConfiguration to configure it.

Communication Center: This is the heart of the DroneController, it provides the actual communication between the internals of the DroneController and the ARDrone. The Communication Center spawns for each communication channel a new thread on which it sets up a UDP or TCP session. Currently four communication channels are supported:

  • The Navigation Data channel: This channel receives information from the ARDrone like status, positional parameters, tag detection info and so on. (UDP)
  • The VideoStream channel: This channel receives the raw data coming from the two video cameras. (UDP)
  • The ATCommands channel: This channel is used to send the commands to the ARDrone. (UDP)
  • The Control channel: This channel is use to send/receive control information to and from the ARDrone. (TCP)

Command Center: This is the place where commands are sent to. These commands are formatted according to the rules defined by the ARDrone and put on a command queue. The Communication Center empties this queue on a predefined interval.

DroneProxy: This represents the ARDrone in the DroneController. Information about the ARDrone is stored at this level. Getting information from the DroneProxy is like getting information from the actual ARDrone.

IDroneCommander: This is the contract that allows consumers to interact with the DroneController. It defines the methods supported by the ARDrone like: Takeoff, Land, SteFlatTrim, PlayLedAnimation and so on. The DroneController implements this contract.

IInputProvider: This is the contract that has to be implemented by device controllers. The contract defines an entry point for the IDroneCommander. In this way can the device controller pass commands to the DroneController.

What does the DroneController support:

  • Pilot the ARDrone via the supported commands (look for further info on the official developer support site of Parrot)
  • Make video recordings of the frames captured by the ARDrone cameras. The output format is WMV.
  • Save the current video frame as pictures. The output format is JPEG.
  • Subscribe to several events that provide vital information (video, connection status, health status, ARDrone info, Tag detection info, …)
  • Several input providers are supported including Wiimote. Additional ones can be easily created by implementing the IInputProvider interface.

What’s in the related projects:

  • A sample of a simple WPF interface using the DroneController.
  • A more advance sample using multitouch features of .NET 4.0 and Windows 7.
  • An experimental extension that uses a Workflow metaphor to design automated flights.
  • Experimental follow me procedure that makes the ARDrone follow a coloured tag (this is something that still needs some redesign and refinement)

The DroneController is written completely in C#, the video recording and wlan connectivity part in it and the Wilke.Interactive.Drone.InputProviders assembly rely on following third party libraries:

This is the version info found on the ARDrone (telnet 192.168.1.1 and then type cat /data/config.ini) :

  • num_version_config = 1
  • num_version_mb = 17
  • num_version_soft = 1.0.4
  • soft_build_date= 2010-07-16 15:12

I did not update to the latest firmware since I read on the forum about some instabilities. This might or might not be the case but I did want to avoid extra complications. According to the latest official documentation no major changes have taken place on the level of AT Commands between the two last firmware updates so the DroneController should work fine with it. If people using the latest firmware do have issues please let me know.

In the next part I will talk about how to use the ARDrone controller.

Introduction

This is part one of a two part article. In the first part I will report on a couple of findings when working with the SPWebConfigModification API, in the second part I will introduce a simulator tool that allows to learn, experiment and generate code for SPWebConfigModification related tasks. This topic is not in any way a replacement for the official documentation, it only serves to highlight a couple tricky aspects.

Abstract

The SharePoint SPWebConfigModification API allows the developer to write code that updates the web.config in a semi transactional way. By semi transactional I mean that changes made to the web.config can be rolled back when a certain condition is met. SharePoint will also take care of making the changes to all web front ends in the SharePoint farm. The uneasy part of working with this API is that it is not that transparent to use and is not that well documented (although for the SharePoint 2010 version this seems to be somewhat better).

The basic steps for modifying the web.config are:

  • Get the current list of modifications
  • Remove existing entries if required
  • Add new entries if required
  • Update modifications
  • Apply modifications

As you can see these basic steps are easy to follow but in real life they can lead sometimes to unexpected situations.

Closer Look

Let’s go over the basic steps and look at these steps from a SharePoint point of view.

Get the current list of modifications

You can store SPWebConfigModification entries on the level of a SPWebService or SPWebApplication. In the former case the changes will be applied to all SPWebApplication instances in the farm, in the latter case the changes will be applied only to the selected SPWebApplication.

Where does SharePoint store the SPWebConfigModification entries for a SPWebService or SPWebApplication? Short answer: in the SharePoint configuration database. Longer answer: in two property bags that are persisted in the ‘Objects’ table in the SharePoint configuration database.

There are indeed two property bags where these modifications are stored, both in the Object table in the farm’s configuration database.

Let’s give an example:

Clean Situation

Suppose I have a farm with one SPWebApplication instance, and no SPWebConfigModification entries have been added. This is the initial clean situation.

I will look up information in the Object table with following SQL statement:

SELECT ParentId, ClassId,Name, CONVERT(xml, Properties) AS Properties
FROM [SharePoint2010_Config].[dbo].[Objects]
WHERE Name LIKE ‘%WebConfigChanges%’

Starting from the initial situation I would get:

tcz3kayz

Why did I use ‘%WebConfigChanges%’ in the query, because this is how SharePoint identifies the property bag that contains changes applied to the web.config.

Some remarks about the resultset:

– I did not print out the full content of the Properties field as this would be too long. But by using CONVERT(xml, Properties) I can click on the link in the properties field and a new window will open with the xml. This avoids you of having to go through the field as text (which is also limited in size).

The default xml looks as follows:

<object type=Microsoft.SharePoint.Administration.SPWebConfigFileChanges, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c>
<fld type=System.Collections.SortedList, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 name=WebConfigChildNodes />
<fld type=System.Collections.SortedList, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 name=WebConfigAttrChanges />
<fld type=System.Collections.SortedList, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 name=WebConfigSections />
<fld type=System.Collections.Hashtable, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 name=m_UpgradedPersistedFields />
<fld name=m_Properties type=null />
<sFld type=String name=m_LastUpdatedUser>WKS-HERMESWilke</sFld>
<sFld type=String name=m_LastUpdatedProcess>psconfigui (3024)</sFld>
<sFld type=String name=m_LastUpdatedMachine>WKS-HERMES</sFld>
<sFld type=DateTime name=m_LastUpdatedTime>2010-08-25T20:59:06</sFld>
</object>

– The name WebConfigChanges – <Guid> is created by the API as follows:

internal class SPWebConfigFileChanges : SPPersistedObject
{
private static string s_BaseName;
static SPWebConfigFileChanges()
{
s_BaseName = “WebConfigChanges – “;
}
///
///
///
internal static string GenerateName(Guid serverId)
{
return (s_BaseName + serverId.ToString());
}
///
///
///
}

So the Guid is actually the id of the SharePoint server (farm).

– The ClassId is an identifier that tells you what class this property bag is associated with, in this case the ClassId points to the SPWebConfigFileChanges class in the Microsoft.SharePoint.Administration namespace, this can be seen also with reflector, look at the Guid attribute on this class definition:

[Obfuscation(Exclude = true, Feature = “renaming”, StripAfterObfuscation = true), Guid(“9B0E7DE4-847D-42cf-A84F-99CD76D4F1E6”)]
internal class SPWebConfigFileChanges : SPPersistedObject
{
/// <summary>
///
/// </summary>
}

Why are there two property bags although I only have one SPWebApplication instance? The second property bag comes from the Central Administration SPWebApplication. This can be traced back by taking the ParentId and executing a query to retrieve the property bag associated with that Id and then looking at the ClassId.

This is a list of some common ClassId’s

SPWebService 45AD2BF2-4E3E-46A1-B477-126944C0ACEF
SPWebApplication 113FB569-7520-4651-8FC4-E9F4F5887618
SPAdministrationWebApplication 4C0FA7BC-0812-4ED2-80AB-89D752898BC6
SPWebConfigFileChanges 9B0E7DE4-847D-42CF-A84F-99CD76D4F1E6
Apply one SPWebConfigModification

Let’s look at the situation where we apply one SPWebConfigModification to the SPWebApplication instance. This is done as follows:

SPWebService contentService = SPWebService.ContentService;
SPWebApplication webApplication = SPWebApplication.Lookup(new Uri(“SharePoint – 80”));

SPWebConfigModification spWebConfigModification = null;
spWebConfigModification = new SPWebConfigModification();
spWebConfigModification.Owner = “[WebConfigModificationTester]”;
spWebConfigModification.Path = “configuration”;
spWebConfigModification.Name = “connectionStrings”;
spWebConfigModification.Value = “<connectionStrings/>”;
spWebConfigModification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
spWebConfigModification.Sequence = 0;
webApplication.WebConfigModifications.Add(spWebConfigModification);

webApplication.Update();
contentService.ApplyWebConfigModifications();

Now execute our query again but this time change the WHERE clause a bit:

SELECT ParentId, ClassId,Name, CONVERT(xml, Properties) AS Properties
FROM [SharePoint2010_Config].[dbo].[Objects]
WHERE Properties LIKE ‘%WebConfigModificationTester%’

This is because as you can see from the code snippet that adds the SPWebConfigModiciation we have set the Owner to [WebConfigModificationTester].

This is the new resultset:

qdpjmvwu

We have two rows in the resultset:

The first row is the property bag of the SPWebApplication (verify ClassId in ClassId table)

The second row is the updated property bag of the SPWebConfigFileChanges instance, the xml looks as follows:

<object type=Microsoft.SharePoint.Administration.SPWebConfigFileChanges, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c>
<fld type=System.Collections.SortedList, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 name=WebConfigChildNodes>
<sFld type=String>configuration/connectionStrings</sFld>
<fld type=Microsoft.SharePoint.Administration.SPWebConfigModification, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c>
<object type=Microsoft.SharePoint.Administration.SPWebConfigModification, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c>
<sFld type=String name=m_Name>connectionStrings</sFld>
<sFld type=UInt32 name=m_Sequence>0</sFld>
<sFld type=String name=m_XPath>configuration</sFld>
<sFld type=String name=m_Owner>[WebConfigModificationTester]</sFld>
<sFld type=String name=m_Value>&lt;connectionStrings/&gt;</sFld>
<fld type=Microsoft.SharePoint.Administration.SPWebConfigModification+SPWebConfigModificationType, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c name=m_Type>EnsureChildNode</fld>
<sFld type=String name=m_PreviousAttrValue />
</object>
</fld>
</fld>
<fld type=System.Collections.SortedList, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 name=WebConfigAttrChanges />
<fld type=System.Collections.SortedList, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 name=WebConfigSections />
<fld type=System.Collections.Hashtable, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 name=m_UpgradedPersistedFields />
<fld name=m_Properties type=null />
<sFld type=String name=m_LastUpdatedUser>WKS-HERMESWilke</sFld>
<sFld type=String name=m_LastUpdatedProcess>ConsoleApplication2.vshost (10408)</sFld>
<sFld type=String name=m_LastUpdatedMachine>WKS-HERMES</sFld>
<sFld type=DateTime name=m_LastUpdatedTime>2010-11-09T15:12:52</sFld>
</object>

Here you see that a new fld element is added with the information added by the SPWebConfigModification entry.

Why is the SPWebConfigModification stored in two places?

The entry in the property bag associated with the SPWebApplication is coming from the statement:

webApplication.Update();

The entry in the property bag associated with the SPWebConfigFileChanges is coming from the statement:

contentService.ApplyWebConfigModifications();

So this means if you only call the first statement no change will be made at that time to the actual web.config file. The entry is only stored in the property bag of the SPWebApplication instance. If at a later stage the ApplyWebConfigModifications is called the changes will be stored in the SPWebConfigFileChanges property bag and propagated to the actual Web.config file.

If you do not call the Update statement strange things can happen, the first time you call ApplyWebConfigModifications the entry will be stored in the SPWebConfigFileChanges property bag and the entry will be added to the Web.config, nothing will be stored in the SPWebApplication property bag though. If you call a second time ApplyWebConfigModifications (without invoking the code that modifies the WebConfigModifications collection) you will notice that the entry will be removed from the SPWebConfigFileChanges property bag and also removed from the Web.Config. An explanation can be found when we look how SharePoint processes the SPWebConfigModification entries.

This means that:

  • Update saves the entries to the property bag associated with SPWebApplication or SPWebService.
  • ApplyWebConfigModifications stores the entries to the property bag associated with the SPWebConfigFileChanges instance and makes the changes to the Web.config.

How does SharePoint apply SPWebConfigModifications?

We have seen that changes to the actual Web.config file are made by the call to ApplyWebConfigModifications. So let’s focus on this method.

Each time you call ApplyWebConfigModifications there are a couple of steps that are performed:

ApplyWebConfigModification

Let’s go over the scenario again where ApplyWebConfigModifications was called without a prior Update in the case where only one SPWebConfigModification is added to the WebConfigModifications collection of the SPWebApplication instance.

If you look at this process you can now understand why the Web.Config change is removed the second time you call ApplyWebConfigModifications without having called Update. The first time you invoke the code, the entry is found in the WebConfigModifications collection of the SPWebApplication instance you are working with and that you changed via code (although it has not yet been updated it is available in memory). So these changes will be applied to the Web.config file and stored in the SPWebConfigFileChanges property bag.

The next time you call ApplyWebConfigModifications (if you did not invoke the code that makes a change to the WebConfigModifications collection) the entry will be removed from the Web.config file (assuming the entry can be found and it is not an EnsureSection entry) and since it is not to be found in the SPWebApplication property bag (either from config database and/or via code) it will not be reapplied. Since at this time the new entries collection is empty an the SPWebConfigFileChanges property bag will not contain any entries anymore.

What’s the difference between EnsureSection and EnsureChildNode

Conceptually EnsureSection is used when you want to be sure that a parent node to which additional nodes have to be added exist. Since in some cases you are not the only one that will add nodes to this parent node it can not be removed. A good example is the connectionStrings node, if you need to add a connectionString as part of a feature deployment you must be sure that the parent node in this case connectionStrings exists. But once the node has been added it is very well possible that other developers have added connectionString nodes to this parent node. If the feature you created is deactivated (assuming the SPWebConfigModification logic is in the feature event receivers) the parent node can not be removed without further complications for other parts of environment.

So EnsureSection should be used for single nodes, only the Path and Name property of the SPWebConfigModification are used.

EnsureChildNode is used when:

  • You need to add a single node, e.g. add connection string entry to connectionStrings tag. This node is under your control and should be able to be removed again.
  • You need to add a xml fragment to a parent node.
Adding and EnsureChildNode entry

When an EnsureChildNode entry is added: Path, Name and Value properties are used. Process flow for modifying the Web.config file:

EnsureChildNode-Add

WarningImportant to note is that SharePoint does not validate whether the Name property can be used to remove the node added by the Value property.
Removing and EnsureChildNode entry

When an EnsureChildNode entry is removed: Path and Name properties are used. Process flow for modifying the Web.config file:

EnsureChildNode-Remove

So here you see that the Name and Value property must be aligned, if not it is possible that you can add an EnsureChildNode entry but never remove it again, or you add multiple times the same node.

In the following article I will introduce an small utility that will allow developers to learn, experiment and generate code for working with SPWebConfigModification entries without writing to the configuration database or changing the Web.config file. It allows you also to get the actual entries stored in the different property bags to provide a more life like experience.

Introduction

The idea behind this blog article is to provide some background info on a subtopic in the context of JPEG compression. From the title you can already guess that the subtopic is about the DCT phase during the JPEG compression. Some time ago I was working on a project where input, coming from a videosource containing encoded image data, had to be decoded to retrieve the full image. The encoding used was some variation of the JPEG compression routine. I had at my disposition the C code that performed the decoding, but in stead of wrapping the existing C code in a library and rely on ‘interop’ I decided it would be interesting to write the decoder in C#.

When doing some research on JPEG compression I frequently encountered this picture:

 

DCT_basis

Some references:

The rest of this article tries to explain the rationale behind this picture and what it stands for in the context of JPEG compression.

Before taking off I want to stress that these kind of routines are not my specialty, I do not have a formal graphics or mathematical background and everything described here is based on my findings and perceptions after doing some research. So occasionally you may find that I am not using the correct terminology or that I am short-circuiting some details, please feel free to bring this to my attention.

A tiny amount of background info

The basic concept of compression routines like JPEG  is that it allows to drop information on an image in such a way that it is not or practically not perceivable by the viewer. In most cases the JPEG routine divides the image in small blocks, each of these blocks is then put through the compression engine. Some  articles that describes this process nicely are:

One of the steps in this compression routine is the DCT. DCT stands for discrete cosine transform. So this step is just a transform step, it is preparing the data so that it can be compressed at a later stage (quantization step)

That small block of data that will be put through the compression engine is actually an 8×8 matrix. This matrix contains 64 data points (for the sake of simplicity we consider that the image is a gray scale image and that the 8×8 pixel matrix only contains one channel, an RGB image would contain contain three channels if not considering the alpha channel)

If we assume that each pixel only needs one byte to encode the value we see that one small data block needs 64 bytes. So compression has to be able to bring this number down.

Let us take the example used in the Wikipedia article:


left[
begin{array}{rrrrrrrr}
 52 & 55 & 61 &  66 &  70 &  61 & 64 & 73 \
 63 & 59 & 55 &  90 & 109 &  85 & 69 & 72 \
 62 & 59 & 68 & 113 & 144 & 104 & 66 & 73 \
 63 & 58 & 71 & 122 & 154 & 106 & 70 & 69 \
 67 & 61 & 68 & 104 & 126 &  88 & 68 & 70 \
 79 & 65 & 60 &  70 &  77 &  68 & 58 & 75 \
 85 & 71 & 64 &  59 &  55 &  61 & 65 & 83 \
 87 & 79 & 69 &  68 &  65 &  76 & 78 & 94
end{array}
right]

The data points in the matrix have values between 0 and 255 which can be translated in a gray scale value (seen besides the matrix). Since the transformation uses the cosine function with values between –1 and 1 (values centered around zero) we also want to center the data around zero so we subtract 128 from that data values, now the range is between –128 and 128.

This is the resulting matrix


begin{array}{c}
x \
longrightarrow \
left[
begin{array}{rrrrrrrr}
 -76 & -73 & -67 & -62 & -58 & -67 & -64 & -55 \
 -65 & -69 & -73 & -38 & -19 & -43 & -59 & -56 \
 -66 & -69 & -60 & -15 & 16 & -24 & -62 & -55 \
 -65 & -70 & -57 & -6 & 26 & -22 & -58 & -59 \
 -61 & -67 & -60 & -24 & -2 & -40 & -60 & -58 \
 -49 & -63 & -68 & -58 & -51 & -60 & -70 & -53 \
 -43 & -57 & -64 & -69 & -73 & -67 & -63 & -45 \
 -41 & -49 & -59 & -60 & -63 & -52 & -50 & -34
end{array}
right]
end{array}
Biggdownarrow y

This is the matrix on which we will apply the DCT, this is the subject of the next part in this series of articles.