nerdblog - musings, art, etc. of Derek Stevens:

Milestone Integration Platform SDK - Schizophrenic by Design


I've been working with the Milestone MIP SDK for the past few weeks, and while it is extremely powerful, and one of the big names in video surveillance software, this library is... how do I say it? Messy, convoluted, schizophrenic, and painful.

Let me walk you through some of the insanity...

A big part of the library is configuring the hardware, rules, users, alarms, etc. for your site. So there's the namespace:

but wait, there's also this namespace which contains only one class:

And then there's the individual item we want to configure, represented by this class:

But it's really just a stub for the classes in this namespace:

and let's not even talk about this weird class:

So you have to use the ConfigurationAPI to get paths from ConfigurationItems and then use the ConfigurationItems.ItemTypeClass constructor to build the full representation of the configuration object by providing that path and the ServerId of the management server.

That's not even the half of it...

Let's say I want to add an AlarmDefinition to an AlarmDefinitionFolder.

There's a class:

But this is just to retreive one off the server.

If I want to create one, I've got to use this class:

But this class has no constructor, inherited or otherwise -- the only way to create an instance of it is to call this:
AddAlarmDefinitionServerTask VideoOS.ConfigurationItems.AlarmDefinitionFolder.AddAlarmDefinition()

So doing something like this I can get an actual instance of this wicked class:
var client = ClientProxyHelper.GetClientProxy();
var folderConfig = client.GetItem("/AlarmDefinitionFolder");
var alarmDefFolder = new VideoOS.Platform.ConfigurationItems.AlarmDefinitionFolder(EnvironmentManager.Instance.MasterSite.ServerId, folderConfig.Path);
var alarmDef = alarmDefFolder.AddAlarmDefinition();

The first line above is using a class directly out of Milestone's examples. The second line uses the ConfigurationAPI to retrieve a ConfigurationItem. Then we use the Path in the configuration and the ServerId of our management server to retrieve the actual AlarmDefinitionFolder and use it to add a new alarm definition.

So now I've got a class where I can manipulate its fields to create the definition I'm after, and then call
to tell the server to create it.

But some of these values refer to other objects in the site configuration, like cameras, event types, events, users, etc... And only some of these "multiple choice" values come with helpers to tell us what our valid choices are. For the rest, we have to go hunting for them.

If I take the example of the properties EventTypeGroup and EventType I have helpers EventTypeGroupValues and EventTypeValues that should tell me what my valid values are, and EventTypeValues should update depending on what EventTypeGroup is set to. That's how it works in the management client.

But if I do something like this:
alarmDef.EventTypeGroup = alarmDef.EventTypeGroupValues.Select(x => x.Value).First();

what do you think I get?

Nothing. I made sure that the value I'm setting EventTypeGroup to is something that has EventTypes associated with it... So this is weird.

But wait there's this inherited method:
List ServerTask.GetValueTypeInfoList(string propertyKey)

Ok if I use it in place of just looking at my helper dictionary above:
alarmDef.EventTypeGroup = alarmDef.EventTypeGroupValues.Select(x => x.Value).First();

Fingers crossed, let's see what we see:

You've got to be kidding me.

Whatever the creators of this library were smoking when they wrote this... I would say I want some, but I'm afraid of what it would do to my code, not to mention my brain!

If I come across a solution to this, I'll update this post, or if anyone knows the solution, drop it in the comments or send me an email!

So, the documentation has:
void UpdateState()
Calls the server to get the fields updated, when the progress is < 100

ValidateResult ValidateItem()
Validate the current content

Yet it's the latter of those which is used to update the fields which are dependent on other fields, while the former is used to update the info after you call Execute().