You are on page 1of 175

C•CURE 9000 Connected Partner Program

SDK Programmer's Guide

3.00 8200-2102-31
A0
April 2022
C•CURE and Software House are registered trademarks of Johnson Controls.

The trademarks, logos, and service marks displayed on this document are registered in the United States [or other
countries]. Any misuse of the trademarks is strictly prohibited and Johnson Controls will aggressively enforce its
intellectual property rights to the fullest extent of the law, including pursuit of criminal prosecution wherever necessary. All
trademarks not owned by Johnson Controls are the property of their respective owners, and are used with permission or
allowed under applicable laws.

Product offerings and specifications are subject to change without notice. Actual products may vary from photos. Not all
products include all features. Availability varies by region; contact your regional sales manager. This manual is proprietary
information of Johnson Controls. Unauthorized reproduction of any portion of this manual is prohibited. The material in this
manual is for information purposes only. It is subject to change without notice. Johnson Controls assumes no
responsibility for incorrect information this manual may contain.

© 2022 Johnson Controls. All rights reserved. JOHNSON CONTROLS, TYCO and Software House are trademarks of
Johnson Controls.
Table of Contents

Introduction 5
C•CURE 9000 SDK overview and integration types 5
How to get C•CURE 9000 SDK 6
Integration process 7
Integration: Knowledge Requirements 9
Integration: Journal and Audit 10
Integration: Enterprise System 10
Integration: Core Libraries 11
Integration: Configure AssemblyInfo.cs File for Connection to CrossFire 11
Integration: Delay Sign and Submit your Integration Assemblies for Strong Signing 12
Integration: Installer 16
Integration: Testing 17
Integration: Deployment into a C•CURE 9000 Production System 20
Integration: Development Support 20
Integration: Support at the Customer Sites 21
Integration: Diagnostic Logs 21
Integration: Troubleshooting Common Issues 22
How to install a C•CURE 9000 development environment 25
System specification 25
System prerequisites 25
Installation steps 25
Verify that the development environment is successfully installed and running 26
Create a custom object and database table 27
Custom object examples 27
Relational mapping 27
Create a Server Component as a Windows Service 33
Building server component extensions into the C•CURE 9000 system 33
Writing a server component 33
Wiring a server component into the C•CURE 9000 notification system 34
Requesting/Persisting objects in the C•CURE 9000 system 35
Create a client component to display in the Administration workstation or Monitoring station 40
Client components 40
Navigation Framework 59
Actions 69
Maps 74
Journal Messaging 75
Journal Messaging Terminology 75
How the Journal Message System Works 77
Journal Message Types 77
Journal Message Format Data 79
Creating Journal Messages 81
Designing and Implementing Journal Messaging 82
Code Sample: Using the Journal Messaging Interface 87
Visual Studio Templates 89
Maintenance-Mode 90
Monitor 95
Show Associations 98
Putting it all Together 101
Personnel Framework 103
Personnel Framework 103
Custom Field Types for Personnel User-defined Fields 104
Using the Personnel Framework in Your Integration 108
Video Framework 110
Video Framework 110
Adding Your Components to the Video Tree 112
Client Support for Video 116
Server Support for Video 126
Third-party integration into SOC View 129
Third-party integration into Video Search 132
Third-party integration with Clip Management 135
Video Feature Support 141
Hardware Framework 147
Adding Your Component to the Hardware Tree 147
Configure Custom Hardware 148
License Check 148
Client Support for Hardware 148
Server Support for Hardware 152
Samples, Tools, and Templates 153
Samples 153
Tools 156
Templates 158
Developing for the Enterprise Architecture 161
Integrating with the Enterprise Architecture Option 161
Discovery/Reporting Tool 162
Appendices 165
Introduction

C•CURE 9000 SDK overview and integration types


The C•CURE 9000 product offers a Software Development Kit (SDK) that allows partners to integrate their products with
C•CURE. The integration could be just a simple connection to C•CURE 9000 CrossFire server to query, add, modify, and
remove objects, or also involve custom objects and UI components that can be displayed within the C•CURE 9000
Administration Workstation and/or Monitoring Station.

Any partner integration with C•CURE 9000 can be successfully integrated in two ways.
■ Driver Service: The partner has to write a Windows Service based server component that communicates with the
CrossFire Server for the sending/retrieval of any information to/from the C•CURE 9000 system. The partner has to fill
in the license form and send it back to Software House Product Management for the ServerComponent driver GUID
to be licensed. The partner will receive the license form when they are registered for the program. For the component
to communicate to the server, in the project’s AssemblyInfo.cs file, the partner must pass in a GUID licensed for the
integration component by the Software House Product Management team. The Server will validate the GUID built
into the Assembly against a matching GUID licensed in the system via Injection by the User into the database and
the application of a C•CURE 9000.TLIC License file containing the same GUID. If the GUID in the Assembly is
recognized by the Server, the Assembly will be authorized by the Server to integrate and communicate with it. For
more information on how to configure your AssemblyInfo.cs file, refer to the AssemblyInfo.cs Configuration section.
■ Client: The partner can connect to the C•CURE 9000 server through a custom client application, whereby said client
serves as the front end to the server. As of v2.80, for Client Connections as well, the partner must fill in the license
form and send it back to Software House for the Client Integration GUID to be licensed. The partner will receive the
license form when they are registered for the program. For the client to communicate to the server, in the project’s
AssemblyInfo.cs file, the partner must pass in a GUID licensed for the integration component by the Software House
Product Management team. The Server will validate the GUID built into the Assembly against a matching GUID
licensed in the system via Injection by the User into the database and the application of C•CURE 9000.TLIC License
file containing the same GUID. If the GUID in the Assembly is recognized by the Server, the Assembly will be
authorized by the Server to integrate and communicate with it. For more information on how to configure your
AssemblyInfo.cs file, refer to the AssemblyInfo.cs Configuration section.

From version 2.80 and higher, each C•CURE 9000 client-only integration needs to be registered, licensed, and is restricted to a
NOTE maximum of 10 Simultaneous Client connections to CrossFire per GUID. Complete the GUID insertion and Licensing Procedure
documented in Appendices. Then, in the Administration Workstation go to Help>About>Licensing. On this tab scroll to the very
bottom of the Options box. You should see your integration option followed by [n/10], where n indicates the number of clients (n),
out of a maximum of 10 Simultaneous Client Seats, that have been consumed.
There are some key differences between GUID requirements and metered connections for CrossFire Client integrations versus
C•CURE Client UI modules:
However, a Client Component displaying in the Admin UI does not consume a metered seat for the integration option.
If a client application is connected to crossfire without a server component performing communication to CrossFire, then a metered
Client Connection will be consumed. For example, the JournalNotificationSample project is a client application that communicates
directly with crossfire server, and consumes one of the 10 metered client seats upon startup and connection to crossfire.

Partners can customize or develop their own objects and UI modules and configure them to display within the C•CURE
Administration Workstation, in various ways:
■ Custom menu options can be added in the Navigation Pane
■ Custom Objects can be created, along with their Editors and Dynamic View
■ Custom Hardware and Video Hardware Components can be configured to display in their respective Trees
■ Custom functionality can be provided for by custom UI fields in existing C•CURE Object Editors or Context Menus
Although such Custom UI Module Assemblies do not require a licensed GUID be built into the Assembly in order to
Display in the Administration Workstation, the integration components they rely on to communicate to the Server will
mandate that an integration GUID be licensed and registered in the System, without which Data passed from the front end
custom components will not be passed to the Server. Please refer to the information on developing a Driver Service for
more information on creating a Server Component that can pass data to the Server.

The integration assemblies are loaded at runtime into the C•CURE 9000 space and are running side by side with the
product's assemblies.

C•CURE 9000 v3.0 and later version will no longer support direct integration with partner’s server component integration using
NOTE InProc DLLs. All server component integrations need to be Windows Services. If your integration’s server component is not in
Windows Services form, please convert it to a Windows service. Otherwise, you will not be able to connect to C•CURE 9000
server. For guidance on how to develop a ServerComponent Windows Service, please refer to the sample
CrossFireSample\ServerComponent project provided with the SDK.

How to get C•CURE 9000 SDK


C•CURE 9000 SDK is included in and can be installed from the Connected Program kit. If you have any question
regarding the Connected Program kit, please contact Software House Product Management team for more details.

The C•CURE 9000 SDK can be installed by selecting the victor Application Server, C•CURE 9000, and Connected
Program Kit check boxes under the installer dashboard of the C•CURE 9000 Connected Partner Program kit. When
selected, the C•CURE 9000 installation will install all necessary SDK files including tools, sample source code, and
documentations needed to develop integration.

The following are important documents that get installed into the \Tyco\CrossFire\SDKDocs folder.
■ ProgrammersGuide.chm
■ ProgrammersReference.chm
■ Connected Partner Program Kit Release Notes
■ C•CURE 9000 SDK User Manual

All user manual specific to the C•CURE 9000 SDK are available in the following folder of the Connected Partner package
download:

\PartnerKit Manuals

All standard user manuals of the C•CURE 9000 Product are available in the following directory of the Connected Partner
package download:

\Manuals\CCure\en-US

You can also access C•CURE 9000 manuals and online Help for more information about C•CURE 9000. To find this
resource in the Administration Workstation, navigate to Help>Contents
Integration process

1. Request the product management team for access to the Connected Partner Portal, so that you can upload
documents, open support cases, and review documentation and tutorial videos, and download our partner kit
development packages. It is essential that at least one developer from your team be granted access to the portal for
direct connection with a member of the SDK Engineering Support team.
https://connectedpartnerprogram.partnerproducts.com/?eid=register
2. For a comprehensive video overview of the Software Download, Installation and Licensing Process, please refer to
Step 2, 3 and 4 of this playlist:
https://youtube.com/playlist?list=PLLE4d6pwmNre1OwSkCtB6zMaRERMuZzcM
3. Once the product management team approves your request to join the portal (Step 2) you will have access to review
Partner Kit Documentation. To access these Release Notes and User Manuals, login to the portal navigate to
Connected Packages> C•CURE.
4. Once you have reviewed the partner kit documentation, and are ready to begin developing a C•CURE 9000
integration via the SDK, please work with the product management team to submit your payment for your Connected
Program Partnership
5. Please fill out and submit your SRS to the Software House Product Management Team. You should be able to
download the SRS template from the Connected Partner Portal, or contact Software House Product Management
team for more details.
6. Once your partnership payment has been processed, you will also have access to the SDK Package Downloads at
the very bottom of the webpage on Step 3.
7. Download the “CCURE v3.00 Partner Package”
8. In the downloaded “CCURE v3.00 Partner Package,” run the setup.exe, and follow the dashboard instruction to install
victorApplicationServer, C•CURE 9000 and the C•CURE SDK.
9. Use the HostIDUtil.exe file provided along with the package to identify your system’s HostID, required for licensing
your system.
10. Fill out and Submit a License Application Form to the Product Management team.
11. The product management team will provide you with a C•CURE 9000 .TLIC license file with your integration option
enabled, as well as an InsertLicenseOption script.
12. Copy your InsertLicenseOption script to a batch file. In the script, replace LOCALHOST with your database instance
name. Below the script, append an @pause so that you can see the results of the script operation. Copy this script to
the Tyco\CrossFire\Tools folder. Double-click to run this file. See Appendices or refer to the Step 4 video in this
playlist:
https://www.youtube.com/playlist?list=PLLE4d6pwmNre1OwSkCtB6zMaRERMuZzcM
13. Copy the C•CURE 9000 .TLIC file to your Tyco\CrossFire\License directory. Run the License Manager Application to
browse to and apply this license file
14. Restart the CrossFire Framework Service and CrossFire Server Component Framework Service
15. In the Tyco\CrossFire\SampleSource directory, you will find a CrossFireSamples.sln with a number of Sample
Integrations that you can refer to for Development Guidance.
16. You can begin developing your integration
17. From version 2.90 and higher, you can test your unsigned assemblies in the Administration Workstation on a
DEVELOPER Licensed System.
18. If you have any questions for the SDK Engineering Support Team related to Integration Software Development,
please open a ticket on the connected partner portal. Go to Support>Request Support>Support Case Add. You
should receive a response from a member of our team within 2 business days.
19. Develop an Integration Test Plan and test your integration against this plan
20. Once you have completed your integration development and testing, and are ready to load your assemblies in the
Production environment, Delay Sign your integration assemblies in Release Mode using the CrossFirePublic.snk. Zip
up these assemblies and attach them to a new ticket submitted to the C•CURE 9000 SDK Engineering team, who
will strong sign those assemblies and return them to you. If your integration assemblies reference any third-party
assemblies or your own company assemblies, the referenced assemblies must also be strong signed by their
authors.
21. Once you have completed your integration development and testing, please work with the Product Management team
to have your integration option approved for Production Licensing. At this point, your integration option will be released
as a purchasable license option.
22. Request a Product License from the Product Management team to test your integration in the Production
environment. Run the InsertLicenseOption script you received in Step 11 (replacing LOCALHOST with the Prod DB
instance name) on your production system, and apply the Production License with your integration option enabled
which you received from the Product Management Team. Restart the CrossFire Services.
23. Deploy your strong signed assemblies in your Production System’s Tyco folders. Again, verify your integration
against your integration test plan in the Production Environment.
24. Once you have completed your testing, submit your integration to JCI product management for approval, along with
your compatibility matrix if applicable.

Integration: Knowledge Requirements


It is recommended that the developer who develops an integration with C•CURE 9000 using the SDK have the following
knowledge before developing the integration:
■ Have a clear understanding of how the C•CURE 9000 product operates, including but not limited to:
• How to install and license the product.
• How to start/stop the CrossFire services.
• How to use the Administration client and Monitoring Station client, including the hardware tree, navigation pane,
dynamic views, and context menu options.
• How to configure objects that relate to the integration with an understanding of parent-child relationships, and how
object data gets stored in the database.

■ Have a clear understanding of the following C# .NET programming language concepts


• Build and debug applications using Visual Studio 2019 or later.
• Understanding of the .NET 4.8 Framework.
— Add and consume properties and methods in your classes.
— Make use of .NET's object-oriented features, such as overriding, overloading, inheritance, interfaces, and
dynamic- link libraries.
— Store, retrieve, and manipulate multiple values using arrays.
— Work with .NET generics.
— Make best use of the .NET Framework's support for collection classes.
— Create and use delegates, and understand how they relate to events.
— Use anonymous types, extension methods, object initializers, and implicit type declaration.
• Create Windows Forms applications.
— Understand how to trap Windows Forms events.
— Understand the basic Windows Forms controls.
• Understand the role of DataSets and DataTables in ADO.NET
• Understand error handling using Exception objects
• Interoperate between managed code and COM components.
• Understand Marshalling and handling unmanaged code.
• Save and restore nearly any object to a stream using serialization.
• Display and edit data using DataGridView control.
• How to create a DLL or Executable file.

Integration: Journal and Audit


C•CURE 9000 object activities (such as activating an event or unlocking a door) performed by an operator via the SDK
are journaled in the same manner as when performed by an operator via the C•CURE 9000 Administration or Monitoring
Station client.

C•CURE 9000 object changes (such as creating a personnel record or deleting an event) made by an operator via the
SDK are audited in the same manner as when made by an operator via the C•CURE 9000 Administration or Monitoring
Station client.

You can implement custom Journal Messages for your integration product. Your product can send customized messages
to the C•CURE 9000 Journal that can be displayed on the C•CURE 9000 Activity Viewer in the Monitoring Station, and
can be viewed during a C•CURE 9000 Journal Replay

Integration: Enterprise System


The C•CURE 9000 SDK supports integrations with C•CURE 9000 Enterprise System, similarly to a C•CURE 9000
Standalone System, with the following exceptions:
■ The integration server component cannot be deployed/run on C•CURE 9000 MAS (Master Application Server) of the
Enterprise System.

Enterprise
Standalone
Integration Method MAS SAS

Server Client Server Client Server Client

SDK Supported Supported Not Supported Supported Supported Supported


■ All custom integration objects have to be local objects that reside on SAS(s) (Satellite Application Server).
■ In a multi-Partition environment, such as an Enterprise environment, a PartitionID must be passed for any
create/read/update/delete (CRUD) operations on any object. Specifying the PartitionID indicates on which Partition
the operable object lives or should live.
■ In non-enterprise multi-partition environments, the PartitionID, if none specified, will be 1 which references the
Default partition. In an Enterprise (MAS/SAS) environment, specifying the PartitionID during a CRUD operation is
required for the request to succeed.

It may take a few seconds or minutes for an object persisted on the MAS to be replicated/synced down to the SAS. This
sync process is based on Microsoft Sync Framework and not controlled by SWH products. The amount of time for sync is
dependent on how much data needs to be synced, how busy your C•CURE 9000 system is, and a few other
environmental factors such as network bandwidth and latency.

For more information, see Enterprise Integration Considerations. You can also refer to this guidance on Developing for the
Enterprise Architecture.

Integration: Core Libraries


Software House partners can create applications that integrate with C•CURE 9000 using class objects and methods in
the libraries referenced in Core SDK Libraries. For a complete reference on CrossFire libraries, refer to the C•CURE 9000
SDK Programmer’s Reference (CCURE-9000-SDK-Programmers-Reference.chm).

Integration: Configure AssemblyInfo.cs File for Connection to CrossFire


The AssemblyInfo.cs file, templated by Microsoft for each of your Assembly C# Projects, must be configured with the
following attributes in order to connect to C•CURE.

Please Note that the GUID string you pass to the GUID attribute and ServerComponentIdentification attributes are not
random or arbitrary GUID strings, but must be the actual unique GUID that has been specifically licensed for your
integration Option by the Product Management team. Please note that in order for your integration to be recognized and
authenticated for integration by the CrossFire Server, a GUID licensed and registered for your integration must be enabled
on your license file as well as injected into the database using an InsertLicenseOption script provided by the SWH
Product Management team. Prior to the following step, you must complete the GUID Injection Procedure. For more
information, see Appendices. Refer to the Step 4 video in this playlist for the same instructions in a visual step by step
format: https://www.youtube.com/playlist?list=PLLE4d6pwmNre1OwSkCtB6zMaRERMuZzcM

Please replace the content highlighted below with information relevant to your integration.
NOTE

The key attributes that must be configured in each AssemblyInfo.cs file are:
// Give your integration Assembly an appropriate Title as follows.
[assembly: AssemblyTitle("SoftwareHouse.CrossFire.Samples.CrossFireSample.ServerComponent")]
// The following attribute enables the Assembly to connect to CrossFire.
[assembly: CrossFireEnabled()]
// Replace the default GUID autogenerated in this file by Microsoft in the Guid Attribute with
your integration’s licensed GUID. After completing the License Injection procedure in the Step
4 Video named above, your integration GUID will be authorized by CrossFire for integration.
Accordingly, only if the GUID applied in this attribute is recognized by CrossFire in the
Ccure9000.TLIC file and in the Database will the assembly be authorized for integration with
CrossFire. If the GUID is not recognized by CrossFire, the Assembly will be rejected for
integration. Note that this GUID requirement exists for all integration components except for
UI only Client Components (i.e. Adding a custom UI component, such as a Navigation Pane menu
item, into the Administration Workstation). However it does not hurt to apply your GUID for
Admin UI Customization Assemblies as well.
[assembly: Guid("F64D393D-D882-4fcf- BDED-FAC8250FB1A8")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: CLSCompliant(true)]
[assembly: ComVisible(false)]
// This CrossFireEnabled attribute must be added to every AssemblyInfo.cs file in order to
connect to and communicate with CrossFire.
[assembly: CrossFireEnabled()]
// For Server components you must include the ServerComponentIdentification attribute. To its
first parameter, you can pass “Access Management” or “Video” based on the type of your
integration. To the second parameter, you must pass the same GUID, licensed for your
integration and injected into your database, as passed above to the Guid attribute.
[assembly: ServerComponentIdentification("Access Management", " F64D393D-D882-4fcf-
BDED-FAC8250FB1A8")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("3.00.1.1")]
[assembly: AssemblyFileVersion("3.00.1.1")]
// Please note that the first two parts of the Assembly Versioning information
should NOT exceed the Ccure Version plus .09. For example if integrating with Ccure
v3.00, your AssemblyVersion and AssemblyFileVersion Major Version and Minor Version
should not exceed “3.09.*”. If this value is exceeded, the assembly will fail the
Licensing Validation, which will cause failure of said assembly to integrate with
Ccure and CrossFire.

Integration: Delay Sign and Submit your Integration Assemblies for Strong Signing
The strong signing of integration assemblies is required to protect the C•CURE Server from injection of uncontrolled
malicious assemblies. Certified Partners are provided, via the Connected Program Kit, a CrossFirePublic.snk key that
must be used to delay sign the integration assemblies before they can be strong signed by the SoftwareHouse SDK
Engineering team. This ensures that only certified partners can develop integrations with C•CURE.

Assemblies to be loaded by CrossFire must be built with the [assembly: CrossFireEnabled()] attribute applied, and such
assemblies must be strong signed with the CrossFire key pair

To prepare your assemblies for qualification to release into Customer Production environments, you will need to Delay
sign the assemblies with the public key “CrossFirePublic.snk” supplied in the \Tyco\CrossFire\SampleSource directory.
Assemblies that extend the following C•CURE 9000 areas need to be strongly named by the Software House SDK
Engineering team using Software House’s matching private secure key before sent back to Software House for
qualification.
■ Server Components
■ Actions that extend C•CURE 9000 events
■ Client Components
■ Navigation items

In Visual Studio, Right Click on your C# project in the Solution Explorer view, and click on Properties in the Context
Menu.
Figure 1:

Go to the Signing Tab and check the boxes ‘Sign the assembly’ and ‘Delay sign only’. Under ‘Choose a strong name key
file’, select the CrossFirePublic.snk Public Key located in the Tyco\CrossFire\SampleSource folder.
Figure 2:

Save the above configurations and build the assembly in RELEASE Mode.

Once you have built your Delay Signed assemblies, attach them in a Zip file to a new Support Case Ticket which you will
submit to the C•CURE 9000 SDK Engineering team via the Connected Partner Portal. A member of the SDK Engineering
team will strong sign your assemblies and return them to you to load on your production system.

If your integration assemblies reference any third-party assemblies or your own company assemblies, the referenced
assemblies must also be strong signed by their authors. For instructions on how to create a Key Pair and Strong Sign your
own company referenced assemblies refer to the following Microsoft documentation:

https://docs.microsoft.com/en-us/dotnet/framework/app-domains/how-to-create-a-public-private-key-pair

https://docs.microsoft.com/en-us/dotnet/standard/assembly/sign-strong-name

https://docs.microsoft.com/en-us/dotnet/framework/tools/sn-exe-strong-name-tool

The Strong Name tool can be used to disable signature verification of a particular assembly by using the -Vr option:
sn.exe -vr Sample.dll

To ensure that your assembly was appropriately STRONG Signed with the SWH private key by the SDK Engineering
team, you can additionally run this command:
sn.exe -vf Sample.dll

Installing the assembly in the assembly cache (GAC) is not a requirement.


NOTE
Integration: Installer
It is recommended the integration have an installer to deploy the integration assemblies into the customer’s C•CURE
9000 system. The integration installer must have at least the following functionalities:
■ Install necessary assemblies on C•CURE 9000 server.
■ Install necessary assemblies on C•CURE 9000 remote clients.
■ Install database tables and/or scripts into C•CURE 9000 SQL database if the integration needs these operations.
Please make sure that the installer will work with both local and remote SQL database.
■ Inject the integration GUID into the C•CURE Database via the same InsertLicenseOption script provided to the
partner by Software House Product Management.

When developing an installer for your integration that requires SQL to deploy tables and or scripts, there are known issues
with hardened systems that have disabled lower TLS versions. Native install technology, such as WIX and InstallShield,
cannot execute native calls to SQL when lower TLS versions are disabled on the operating system. To work around this,
use a custom action to execute your SQL statements. If you are developing using .NET, use ADO.NET in a custom
action.

Ensure that the integration installer sets up the service to run as the domain account that installed the integration, not the
logged in local user account.

If the service is not running on a system that uses the local system account, but instead runs on a domain account,
ensure the service installer is set up to run the service as the domain account, not the local system account.

It is recommended that your installer programmatically inject the Database Connection string into the
SoftwareHouse.CrossFire.Server.exe.config. For guidance on programmatic connection string injection, see the sample
below .

The ccureDataSourceStr is the sql path which can be detected from the registry.
NOTE

public static void configureConnectionString(string ccureDataSourceStr){


ConnectionStringSettings newConnectionString = new ConnectionStringSettings();
//Replace the ConnectionString Name with the desired value.
newConnectionString.Name =
"SampleIntegration.NextGenConnectedProgram.Access.Objects";
newConnectionString.ConnectionString = String.Format("DATA SOURCE={0};INITIAL
CATALOG=ACVSCore;INTEGRATED SECURITY=TRUE", ccureDataSourceStr);
//If your Tyco folder has been installed to a different directory, specify it
accordingly.
string installDir = @"C:\Program Files (x86)\Tyco";
//need the exe file full path
string exeFile = Path.Combine(installDir, "Crossfire",
"SoftwareHouse.CrossFire.Server.exe");
injectNewConnectionString(newConnectionString, exeFile);
}
public static void injectNewConnectionString(ConnectionStringSettings
newConnectionString, string exeFile)
{

try

//get configuration file information.

Configuration myConfigFile = ConfigurationManager.OpenExeConfiguration(exeFile);

ConnectionStringsSection connectionStrings = myConfigFile.GetSection


("connectionStrings") as ConnectionStringsSection;

//Add and Save will automatically encrypt the new connectionString.


if (connectionStrings != null)
{
myConfigFile.ConnectionStrings.ConnectionStrings.Add(newConnectionString);
myConfigFile.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("connectionStrings");
}
else{Console.WriteLine($"injectNewConnectionString - connection string section
is null");

}
}
catch (Exception e)
{
Console.WriteLine($"injectNewConnectionString - Failed to add new
connectionStringSettings, Exception {e.Message}");throw;
}
}

Integration: Testing
You must test your integration thoroughly before submitting the integration final deliverables to the Software House
Product Management team to approve to release to C•CURE 9000 customers.

Testing the integration on your C•CURE 9000 development environment during the development
effort:
Starting in version 2.90, the Partner DLL Signature Passage option enabled on your DEVELOPER license allows you to
integrate unsigned assemblies with the Administration Workstation in the development environment.

This flexibility will allow you to fully test your integration easily with your development environment before submitting your
integration assemblies to the SDK Engineering team for strong signing to run on a C•CURE 9000 production system.
Integration Components must be installed to these folders based on their function. Integration component DLL or EXE
files for client components, server components, and object libraries must be placed in specific directories after being built
in order to be detected by the C•CURE Administration Workstation and Server Configuration Application.

Object Libraries

Location – C:\Program Files\Software House\Tyco\CrossFire


■ Additionally copy Object Libraries to the directories of other Assemblies referencing these Objects

Client Components

Location – C:\Program Files\Software House\Tyco\CrossFire\CCURE Client

Server Components

Location – C:\Program Files\Software House\Tyco\CrossFire\ServerComponents

For instructions on installing the DriverService, see Installing a Driver Service.

Configure the CrossFire Simulator Software


To help in testing integrations with C•CURE 9000, on C•CURE 9000 development system, installed using the Connected
Partner package, with a Developer license with serial 9-99999 applied, the CrossFire Simulation Software can be used to
simulate activity journal messages such as CardAdmits, CardRejects, Door State Changes, and Manual Actions. For
example, it can be used to help validate that, when you register for RealTime Journal Notifications, you receive the flow of
Card Swipe events in real time. Please note, however, that the Reason Codes provided in a Simulated Card Activity do
not represent true reasons for Card Rejection.

The CrossFire Simulation Hardware should not be relied upon as a substitute for physical hardware to test hardware related
NOTE scenarios such as state changes or alarms. With that said, here are the steps to expose the Simulation tab on the
ServerConfiguration Application, and configure Simulation Hardware and Personnel related Objects for Journaling purposes.

To expose the Simulation tab on the Server Configuration Application, click very fast multiple times right under the red (x)
in the top right corner of the Server Configuration Application screen.
Figure 3: Accessing the simulation tab

To simulate scenarios like opening or closing doors, and admit/reject badge swipes, complete the following steps:
1. In the Server Configutation Application, select the Simulation tab.
2. On the Simulation tab, select the Access subtab.
3. In the Personnel tab, under Personnel, select the Enable check box and click Create.
4. Then in the iStar tab, select the Enable check box and click Create.
5. On the Generic Hardware tab, under Input, select the Enable check box and then click Create.
6. On the Generic Hardware tab, under Door, select the Enable check box and then click Create.
7. On the Personnel tab, under Clearance, select the Enable check box and then click Create.

After this you will be able to Force/Open/Close/Hold Doors (on the Generic Hardware Tab) and simulate Admits/Rejects
on the Personnel tab.

If you configure XFEvent on the Event tab, you can also Activate/Deactivate/Arm/Disarm/Pulse events.

You only need to Create all of the simulation objects once, but each subsequent time you would like to simulate Journal Activity you
NOTE must click the check boxes to Enable all of the above configurations.

You can also look at the following video for more information on how to use the simulator: https://youtu.be/3dKe1sKffYM
Testing the integration on a C•CURE 9000 production system (After completion of Development
system testing)
In order to fully test any integration functionality within a live C•CURE 9000 instance prior to release, ensure the following
criteria is met, or the integration will not work.
■ C•CURE 9000 must be properly licensed with a production license serial # 9-xxxxx that has your integration license
option enabled.
■ The GUIDs and object names used in your assemblies must match those specified on the license form that you
submitted to Software House in order to receive the license file. For new integrations: During the license acquisition
and registration process, the JCI-Connected Program team provides an InsertLicenseOption script to the partner. The
partner needs to run this script to register their license option with any C•CURE 9000 system in production that uses
the integration.

Partners are responsible for developing and executing the complete test procedures. Software House only provides an
integration testing guideline (which can be downloaded from the Connected Partner portal), but is not accountable for the
content or result of any test case. It is the partner’s responsibility to develop and execute all test cases.

The Connected Program team has a C∙CURE 9000 enterprise environment setup in a lab at the JCI facility in Westford,
MA, USA that partners can schedule to visit in advance to use for testing, if needed. Please talk to the Software House
Product Management team if you need more information or to schedule a visit.

Integration: Deployment into a C•CURE 9000 Production System


For an integration to be deployed and run successfully on a customer's C•CURE 9000 platform, all following conditions
must be met:
■ Integration assemblies must be built and tested against the desired C•CURE Version in Development first, prior to
releasing those same assemblies to a Production environment configured with said C•CURE version
■ The integration assemblies are digitally signed by the Software House SDK engineering team.
■ The integration must be approved by the Software House Product Management team and listed on the Connected
Partner’s compatibility matrix to enable the ability to generate a Production license for the integration to apply on a
C•CURE 9000 production system.
• If the integration has not be approved and listed on the Connected Partner’s compatibility matrix, the integration
will not be recognized and preliminarily supported by Software House Technical support team.
■ The integration ClientID (GUID) has been inserted into the customer C•CURE 9000 database.
■ The integration license option has been enabled on the customer C•CURE 9000 Production system license. Please
discuss with the C•CURE 9000 system’s administrator or authorized dealer for how to Order an updated license for
the customer production system to enable your integration license option.

Integration: Development Support


During the development of your SDK integration:
■ If you have any questions regarding the SDK, open a support case ticket on the Connected Partner portal website at
https://connectedpartnerprogram.partnerproducts.com
■ If you have any question regarding the normal features of C•CURE 9000 product and Database installation and
configurations, please contact the system administrator or integrator of C•CURE 9000 system you integrate with.
They should be able to help you.
You can also reach out to the main C•CURE Technical Support team for assistance on using the contact information
at this link:
https://www.swhouse.com/support/Contact_Technical_Support.aspx
(The party that contacts the C•CURE Technical Support team must be C•CURE certified by SWH to receive
support. Ensure that they have the SSA, Serial Number and Certification number available before making the call.)

Integration: Support at the Customer Sites


Since an integration is developed by the integration partner, if our C•CURE 9000 end-users/customers report any issue
with the integration, the integration partner will need to troubleshoot and provide the support directly to our C•CURE 9000
end-users/customers. If the integration partner finds any reproducible issue with the CrossFire SDK, please log a ticket in
the Connected Partner portal and work with the Connected Partner Program team.

It is required that the integration team work with the Software House Product Management team to submit all deliverables
(including contact information of the integration support team) to list the integration on the Software House – Connected
Partner – Compatibility matrix. In the case that C•CURE 9000 end-users/customers contact our C•CURE 9000
Technical Support team regarding an issue with an integration, the C•CURE 9000 Technical Support team will help to
screen the customer system to make sure the appropriate integration has been installed and licensed correctly, then
transfer or redirect the support case to the integration team based on the information provided by the integration partner.

Integration: Diagnostic Logs


All standard C•CURE 9000 CrossFire diagnostic logs are included in system trace log files under the
\Tyco\CrossFire\Logging folder.

You can further customize Trace logging for deeper diagnostics by enabling Trace Switches in the Server Configuration
Application.
1. To expose the Diagnostic Tab in the Server Configuration Application, click very fast repeatedly under the ‘x’ in the
top right corner.
2. Once the Diagnostics tabs is exposed, Click the ‘Connect’ button in the top left of the tab until ‘All Switches” are
exposed.

To turn on individual Trace Switches, click on them until they turn Green. Selecting the check box next to the trace switch
will ensure that, if the CrossFire Framework and CrossFire ServerComponent Framework Services are stopped and
restarted, that Trace Switch will be turned on automatically upon restart of these CrossFire Services.

Some of the frequently used TraceSwitches include ConsentAuthority, CurrentProductLicense, and


CurrentPlatformLicense.

The ConsentAuthority TraceSwitch enables logging to indicate operators making client connections to crossfire with a
particular GUID, along with the Privilege and Schedule configured for that operator.

The CurrentProductLicense and CurrentPlatformLicense Trace Switches will show you the status of the client count
every 10 seconds in Debug mode, and every 2.5 minutes in Release Mode.
It is also recommended that partners develop additional diagnostic logs within the integration to help with troubleshooting
when needed.

Integration: Troubleshooting Common Issues

Symptom Resolution

Your Assemblies load successfully in the Development environment, but fail to Your integration assemblies, or any third-party assemblies
load in the Production or Customer environment. they reference may not be strong signed. In the case of a
Failed Signature Check related Load Failure in Crossfire,
verify whether your assembly is strong signed like so:
sn.exe -vf FilesToBeSigned\sampleintegration.dll
You may not have run your InsertLicenseOption Script on
the new system.
Your integration Option may not yet have yet been enabled
in Production, and you were issued a license without your
Integration Option enabled

Integration Fails to Connect to Server You may not have applied your integration GUID in the
GUID attribute and ServerComponentIdentification
attributes of the AssemblyInfo.cs file.
Your GUID has not been inserted into the database by the
InsertLicenseOption script and/or you are still using a license
that was generated before registration of your Integration
Option and your current License file does not have your
option enabled.
You applied an AssemblyVersion and AssemblyFileVersion
in the AssemblyInfo.cs of your project that exceeds the main
C•CUREversion with which you are integrating. i.e., when
integrating with C•CUREv3.0, the first two part of your
AssemblyVersion and AssemblyFileVersion should be no
greater than 3.09*. For more details on Assembly Version
formatting expectations from Microsoft, refer to the section
on Configuring the AssemblyInfo.cs file for Connection to
CrossFire.
The CrossFireFrameworkService and
CrossFireServerComponentFrameworkService are not
started up.
The maximum number of CrossFire Client seats have been
consumed. To verify this, go to the Administration
Workstation. In Help>About>Licensing, in the Options table
if you see [10/10] next to your integration Option name, you
have consumed the maximum number of simultaneous
clients for your option on the CrossFire Server.
Symptom Resolution

You see the “Invalid Service - please consult the SystemTrace log” next to your Your assemblies may not be strong signed yet. After
ServerComponent Service in the ServerConfiguration Application. installing your windows service if you are still able to start up
and run your service successfully through Windows
Services, then getting your assembly strong signed should
resolve this message.
If Strong Signing the assembly does not resolve this
message, check the SystemTrace logs to see whether your
Service is associated with an “invalid windows service name”
message. If so ensure that, in your class that extends the
SoftwareHouse.CrossFire.Common.Core.ServerCompon
ent class, you override the WindowsSerivceName to return
the same string name defined in your installer code for
System.ServiceProcess.ServiceInstaller.ServiceName (and
DisplayName).

TycoESS will not run or crashes. You encounter the error message “TYCOESS: 1. Using Windows Services -> Stop TycoESS
The desired vendor daemon is down” in the License Manager Application 2. Copy C•CURE 9000 .LIC from the x64 folder up one
level to Tyco\CrossFire\License
3. Using Windows Services -> Start TycoESS
4. Now apply and validate your license (TLIC file) which
you put in the Tyco\CrossFire\License

Your unsigned ServerComponent integrates with C•CURE without issue when You may need to UNBLOCK assemblies that were built or
the assembly is unsigned, but fails to load by CrossFire and integrate with signed on a different system.
C•CURE after being strong signed. Figure 2.4.2
Symptom Resolution

Your driver service starts up and then stops, indicating in the System Trace log Ensure that your ServerComponent’s AssemblyInfo.cs file is
that the Server Component is not enabled via Windows Services, but loading the appropriately Configured to meet the requirements for
ServerComponent assembly in the CrossFire\ServerComponent directory Connection to CrossFire.
causes the ServerConfiguration application to crash.

You may see this exception message upon failure to load your If you keep a servercomponent as a dll it will be attached in
ServerComponent: the crossfire memory. A ServerComponent crash can cause
System.NullReferenceException: Object reference not set to an instance of an the CrossFireServer to crash with no control, so Windows
object. At Service is the recommended approach to design your
SoftwareHouse.CrossFire.ServerComponentFramework.WindowsService.On ServerComponent because it will not stop the entire
Start(String[] args) crossfire system in case your integration crashes. That is
why we recommend Windows Service. In version 3.0 you
are required to convert your ServerComponent to a
Windows Service

CrossFire Services fail to start You may have changed the password of the user account
that installed the victor Application Server. Go to Windows
Services, and next to EVERY CrossFire Service click on the
account in the Log On As Column, in the popup go to the Log
On tab and enter the updated password. Then attempt to
restart each associated CrossFire Service.

If these basic troubleshooting steps do not help you, please log a support ticket in the Connected Partners Program portal
to get help from the Connected Partners team. Please select ‘C•CURE 9000 ’ from the Integration type dropdown menu to
ensure that your ticket gets routed and escalated to the correct SDK support team.

For instructions on Debugging your integration, see Debugging a C•CURE 9000 Application.
How to install a C•CURE 9000 development environment

System specification
The C•CURE 9000 SDK and integrations will work with all the Operating Systems supported by C•CURE 9000 product.
Refer to the C•CURE 9000 3.00 release notes for more information on supported Operating Systems.

System prerequisites
The C•CURE 9000 Connected Partner kit has the following requirements for a developer workstation:
■ Windows Server 2019 Standard and Enterprise editions, 32-bit Operating System
■ Windows Server 2019 Standard and Enterprise editions, 64-bit Operating System
■ Windows 10 Professional and Enterprise editions, 32/64-bit Operating System
■ MS SQL Server 2014 Express/Standard/Enterprise
■ SQL 2016 on Windows 10 to support newer customer and SQA databases
■ SQL 2019 on Windows 10 or Windows Server 2019 to support newer customer and SQA databases
■ Visual Studio 2019 v16.4.4 or higher
■ C# .NET 4.8 & .NET Core SDK 3.1
■ IIS (Internet Information Services) 8.0 for enabling 1-Click deployment. This is only needed if the integration also used
any feature related to victor Web Services such as C•CURE Web, C•CURE Portal, or C•CURE Visitor
Management.

Installation steps
Perform the following steps to install the C•CURE 9000 SDK from the Connected Partner kit:
1. Download the ISO file from the installer software. Double-click Setup.exe to open the installer dashboard.
2. Select the victor Application Server, C•CURE 9000, and C•CURE 9000 SDK check boxes.
3. Click the Next button and follow the prompts in the installer dashboard to complete the installation.
For information on how to download and the details of each step in the installation, please refer to the step 2 video in
this video playlist:
https://www.youtube.com/playlist?list=PLLE4d6pwmNre1OwSkCtB6zMaRERMuZzcM

In order for your integration to be recognized and authenticated for integration by the C•CURE 9000 server, a ClientID (GUID)
NOTE needs to be registered for your integration. This ClientID must be enabled on your license file as well as injected into the database
using an InsertLicenseOption script provided by the SWH Product Management team. To apply the license and inject the
integration GUID into the C•CURE 9000 DB, please see Appendices. You can also refer to the Step 4 Video in the following
playlist:https://www.youtube.com/playlist?list=PLLE4d6pwmNre1OwSkCtB6zMaRERMuZzcM

If you are upgrading your C•CURE 9000 SDK development environment from version 2.80 or earlier, refer to the important
integration upgrade instructions in Upgrading development environment from 2.80 and lower.
You also need to install the .NET Framework 4.8 Developer Pack. In case you encounter OS compatibility issues when
installing the latest released Developer Pack version on Windows Server 2019, you can use an older version of the Dev
Pack installer, namely version 4.8.3761.0:
https://download.visualstudio.microsoft.com/download/pr/7afca223-55d2-470a-8edc-
6a1739ae3252/c8c829444416e811be84c5765ede6148/ndp48-devpack-enu.exe

Verify that the development environment is successfully installed and running


The C•CURE 9000 Connected Partner kit installs components in the following locations relative to the C•CURE 9000
installation. The paths below presume an installation on the C:\Program Files(x86) directory.
■ Verify the below folders are installed:
• Object Libraries: \Tyco\CrossFire
• Client Components:\Tyco\CCURE Client
• Server Components: \Tyco\CrossFire\ServerComponents.
■ Navigate to the installed Tyco folder, and ensure that you see all of these subfolders:
• CrossFire\SDKDocs: SDK User Manuals and Documentation
• CrossFire\SampleSource: Sample Integration Source Code and Public Key for delay signing
■ Make sure the Video templates are installed to this directory:
• Templates: C:\Users\[username]\Documents\Visual Studio 2019\Templates\ProjectTemplates\Visual C#
• For more information about installing and using the Templates, see Visual Studio Templates.

The v3.0 Connected Partner Package download’s CCURE_CPK_Partner_ISO folder contains a


NOTE SampleVideoIntegration subfolder with a functioning VideoTreeExtenderSample.sln built using the
aforementioned Video Templates and re-targeted for compatibility with .NET Framework 4.8. Said sample
can be used as a Video Integration development reference instead of building a new integration project
using the foregoing templates.

■ In the Server Configuration Application, make sure that both CrossFire Services are up and Running
Create a custom object and database table
The following is a demonstration on creating custom objects and mapping them to a table inserted into the CCURE
Database. To view the same demonstration in video format, watch the Creating Objects Walkthrough video available on
the Connected Partner portal.

Custom object examples


■ Wireless lock object
■ Key object for the Key management integration
■ Intrusion panel object.

Relational mapping
Relational mapping is a programming technique for converting data between incompatible type systems in databases and
object oriented programming languages.

Building objects that integrate in the C•CURE 9000 SDK space


In order to solve the most fundamental problem in an enterprise system, a developer must be able to create objects that
can be passed throughout the system and persisted in a data store. In the C•CURE 9000 system, this is accomplished by
creating a relational mapping of object to database table. At the object level this is done using a base class called
DataServiceObject. This class encapsulates all the necessary mechanisms for serialization and storage of derived
property values. Any class that is to be processed by the C•CURE 9000 subsystem must derive from
DataServiceObject. Certain attributes provided by the C•CURE 9000 system also must be applied to the derived class to
specify relational mappings to the data store table.

The integration code must not modify native C•CURE Object type names or C•CURE hardware directory structures.
NOTE

The tasks you must perform to integrate your class into C•CURE 9000 are:
1. Create an Interface to define the new object
2. Create a partial class that will map the object to a database table. Define your private members, custom properties,
etc.
3. Create a table in the database to represent the object

Creating an interface definition

The first step in creating a C•CURE 9000 compliant object is to create an interface that defines the new object. As an
example, we will create an object called MyDso defined by IMyDso.

ObjectId and ClassType uniquely identify an object within C•CURE 9000. These two properties used in the C•CURE
9000 system must be implemented. ClassType is implemented in the DataServiceObject base class so that leaves only
ObjectId that must be implemented in your derived class.

On your DSO’s Interface, add the following using directives:


■ SoftwareHouse.CrossFire.Common.DataServiceLayer
■ SoftwareHouse.CrossFire.Common.DataServiceLayer.Import
■ SoftwareHouse.NextGen.Common.SecurityObjectDefinitions

Your Dso interface should be a public interface that extends the IDataServiceObject interface.
Figure 4: IDataServiceObject

Generating a partial class

Now we need to relate the interface we created to a table that will be stored in the database for this object so that the
C•CURE 9000 system knows how to persist an object derived from this interface. Make this class a public partial class.

Your DSO class should extend the DataServiceObject class and implement your custom Dso interface. Add the needed
persistence properties in your DSO class such as ObjectID, GUID, etc. Additionally, add the using directives for inclusion
of the following types:
■ SoftwareHouse.CrossFire.Common.Core
■ SoftwareHouse.CrossFire.Common.DataServiceLayer
■ SoftwareHouse.CrossFire.Common.DataServiceLayer.SaSObjectConversion
■ System.Data
Figure 5:

Creating a database table

The next step is to create a table in the database to which we will relate the created interface definition.
1. Write a SQL script to create a table for this DSO in the ACVSCore database and denote the columns for the included
properties. Create a new Query to setup the schema and table for your DSO object.
In this example the Schema has been titled “Samples” and Table has been titled “MyDso.” The table has two
columns, ObjectID and GUID.
Figure 6:

2. Execute the script and your Schema/Table and table will be created in the database.
Figure 7:

3. Next you need to add your database connection string in your project’s AssemblyInfo.cs. The following modifications
are to be made to this file.
■ Include the using directive at the top for SoftwareHouse.Crossfire.Common.Core.
■ In the GUID Assembly attribute replace the existing GUID with your integration’s unique licensed GUID.
■ Also set the below assembly attributes. Note that the SchemaName should match the name of the schema of the
table you created:
assembly:CrossFireEnabled()]
assembly:SoftwareHouse.CrossFire.Common.DataServiceLayer.DbConnection("DATA
SOURCE=.;INITIAL CATALOG=ACVSCore;INTEGRATED SECURITY=TRUE", ProviderName =
"System.Data.SqlClient", SchemaName = "Samples")]
■ It’s recommended that you programmatically Inject Database Connection String into
SoftwareHouse.CrossFire.Server.exe.config file. Refer to the section on developing an Integration Installer.
Figure 8:

Refer to the Microsoft Guidelines on constructing the AssemblyVersion and AssemblyFileVersion information in the
AssemblyInfo.cs file.

Further, your assembly version must not exceed the value of 3.09.*, in order to be compatible with C•CURE v3.00.

Best Practices
A new set of methods that help to Get or Set property values in DSO objects are available via your project’s reference of
the SoftwareHouse.CrossFire.Common.Objects.dll. These simplifying getter and setter methods are available
for properties of a variety of types, including Bool, ClassType, DateTime, GUID, ObjectKey, Int, and String.
The following is an example usage of the GetProperty and SetProperty methods.
[PersistentProperty("ApplicationLayoutID", DbType.Int32, Nullable = true, IsIndex =
false)]
[ToolTipText(typeof(NewDso), "ApplicationLayoutTipText")]
[ControlIntegerRangeValidation(1, 2147483647)]
public virtual int ApplicationLayoutID
{
get => this.GetPropertyInt(nameof(ApplicationLayoutID));
set => this.SetProperty(nameof(ApplicationLayoutID), value);
}
[PropertyCacheEnabled(nameof(ApplicationLayoutID))]
[ToolTipText(typeof(NewDso), "ApplicationLayoutKeyToolTipText")]
public ObjectKey ApplicationLayoutKey
{
get => this.GetPropertyObjectKey(typeof(ApplicationLayout), nameof
(ApplicationLayoutID));
set => this.SetProperty(typeof(ApplicationLayout), nameof(ApplicationLayoutID),
value);
}

C# allows the usage of the nameof(PropertyName) language expression instead of “PropertyName” .

i.e.
get => this.GetPropertyObjectKey(typeof(ApplicationLayout), nameof
(ApplicationLayoutID));

versus
get{
object val = this["ApplicationLayoutID"];
}

Using the nameof() operator is preferable because It helps to find all the places in the code where the specified property
name is used.
Create a Server Component as a Windows Service
The following sections describe the process of building a server component.
■ Building Server Component Extensions into the C•CURE 9000 System
■ Writing a Server Component on Page 41
■ Wiring a Server Component into the C•CURE 9000 Notification System
■ Requesting/Persisting Objects in the C•CURE 9000 System
■ DataServiceObject
■ DataServiceCollections
■ Attributes

Building server component extensions into the C•CURE 9000 system


One of the most important extension points of the C•CURE 9000 system is Server Components. Server Components are
custom code modules that are integrated into the server space of the C•CURE 9000 system through the Server
Component Framework. The Server Component Framework is a Windows service that interrogates a directory location
for types that derive from a well defined base class called Server Component. Integrators, by deriving their Server
Components from the ServerComponent base class and implementing a couple of abstract methods, immediately
become Server Component Framework friendly. The bottom line functionality that the Server Component extension point
gives the integrator is the translation bridge between their protocol-specific components and the C•CURE 9000 system.

The tasks you need to perform to build a Server Components Extension are:
1. Writing a Server Component
2. Registering the Server Component for Notifications
3. Writing code that calls the DataServiceLayer to Request/Persist objects in the database

Writing a server component


The first step is creating a class and adding a reference to SoftwareHouse.CrossFire.Common.Core.dll located in the
C•CURE 9000 root directory. After creating the class, you derive your class from
SoftwareHouse.CrossFire.Common.Core.ServerComponent. Below is a sample:
public sealed class SampleServerComponent : SoftwareHouse.CrossFire.Common.Shared.
ServerComponent
{
/// <summary>
/// Connects this Server Component into the Server Component
/// Framework.
/// </summary>
public override void Connect()
{
// TODO: Perform initialization of all Server Component Objects
// and startup processing.
}
/// <summary>
/// Shutdown method instructs the Server Component to stop
/// processing and shutdown.
/// </summary>
public override void Shutdown()
{
// TODO: Perform shutdown of all Server Component Objects and
// cleanup.
}
/// <summary>
/// Heartbeat called on a timer from the server side by the Server
/// Component Framework located in the CrossFire Framework Service
/// process.
/// </summary>
public override bool Heartbeat()
{
return true;
}
}

We can add defining characteristics of our new Server Component by editing the ApplicationInfo.cs file and adding
special tags that are recognized by the C•CURE 9000 system. The two that we want to add for our Server Component to
integrate into the system are:
■ [assembly: ServerComponentIdentification("Access Control", “0FAD…”)] – The ServerComponentIdentification
attribute allows the integrator to separate the Server Component into a category that is appropriate to describe the
functionality of their custom code. The first argument is the category of the server components in the server
configuration application. The second argument is a unique identifier or the GUID of the server component assembly
into your integration.
■ [assembly: CrossFireEnabled()] – The CrossFireEnabled attribute is extremely important because without it, the
types defined within the assembly will not be parsed and be able to be used within the C•CURE 9000 system.

Both these attributes are defined in SoftwareHouse.CrossFire.Common.Core.dll.

Once these two steps are performed, and the compiled assembly is placed in the ServerComponents directory in the
C•CURE 9000 root, it will be integrated into the C•CURE 9000 system the next time the CrossFire Server Component
Framework is started.

Wiring a server component into the C•CURE 9000 notification system


The C•CURE 9000 contains a robust notification subsystem that propagates object level changes throughout the system.
The root of the notification structure is a standard EventHandler model with sender and EventArgs. The base notifications
all derived from System.EventArgs that exist in the system are:
■ ObjectCreatedEventArgs: Sent when an object of a requested type is created and saved in the C•CURE 9000
system.
■ ObjectChangingEventArgs: Sent when an object of the requested type is about to be updated in the system. This is a
synchronous notification type allowing consumers the ability to block the proposed update.
■ ObjectChangedEventArgs: Sent when an object of the requested type is updated in the system.
■ ObjectDeletingEventArgs: Sent when an object of the requested type is about to be deleted in the system. This is a
synchronous notification type allowing consumers the ability to block the proposed delete.
■ ObjectDeletedEventArgs: Sent when an object of the requested type has been deleted in the system.

As was mentioned above, there are two different types of notifications in the system: Asynchronous and Synchronous. If
a notification type is synchronous, the C•CURE 9000 system will notify all consumers of the proposed operation and any
consumer can throw an exception in the delegate to block the operation and thus from a system point of view it didn’t
happen. The asynchronous notification is processed after the operation has been committed and the consumer cannot
alter the process based on these notifications.

The consumer registers for notifications by type. Consumers may, although it is not recommended, register for a certain
notification type for ALL types. Consumers may also determine if they would like to have the actual object that the
operation was performed on be sent or just the ObjectKey of the object. The ObjectKey (defined in
SoftwareHouse.CrossFire.Common.Core.dll) of the object is a smaller payload object that contains type and identifier
values that allow the consumer to get information about the object. Consumers may also filter notifications by only
requesting certain property operations be notified. For example, if you wanted to only know when a certain type’s
“Enabled” property changed, you could register in such a way to only get notified in this case.

SoftwareHouse.CrossFire.Common.ClientServiceLayer.dll is a class called ClientServerConnection that makes calls to


the server very simple. Once instantiated, the class takes care of all server calls and notification callbacks. Registering
for notification using the ClientServerConnection class is done with the following:

RegisterTypeClientCallback(Type callBackType, string[] types, EventHandler handler, NotificationReturn returnMethod,


List<string[]> filterNotificationsBy)
■ callbackType – The specific System.EventArgs derived notification type being requested.
■ types – The list of types the consumer cares about being notified about.
■ handler – The System.EventHandlerDelegate in the consumer’s code to call back on when a notification occurs.
■ returnMethod – Specifies whether the consumer would like to receive the ObjectKey of the object being sent or the
actual object.
■ filterNotificationsBy – The optional list of properties to filter the notifications by.

RegisterObjectKeyClientCallback(Type callBackType, ObjectKey key, EventHandler handler, NotificationReturn


returnMethod, string[] filterByProperties)
■ Everything is the same as detailed above except for the key parameter. This represents the ObjectKey of the object
requested.

Requesting/Persisting objects in the C•CURE 9000 system


The middle tier of the C•CURE 9000 system that sits above the database and below the ClientInterfaceLayer is called the
DataServiceLayer. The DataServiceLayer is responsible for maintaining database independence, persisting object data to
the data store, and retrieving stored data from the data store and building objects from it returning them to the caller. The
DataServiceLayer adheres to the factory pattern of object creation where the caller must call the DataServiceLayer to
create all DataServiceObject derived objects to guarantee correct object instantiation and initialization. The basic
methods of the DataServiceLayer are:
CreateObject – Returns a properly initialized object of the requested DataServiceObject type. FindObject – Returns one
distinct DataServiceObject derived object matching the passed in criteria.
■ FindCollection: Returns a collection of DataServiceObject derived objects of the same type (or base type) matching
the passed in criteria.
■ FindComplexCollection: Returns a collection of collections that are related in some way that the caller requests and
populated with objects that match the passed in criteria.
■ Persist Object: Takes the current state of an object and persists it to the data store. Depending on the state of the
object, the appropriate action is performed on the data store (Insert, Update, or Delete).
■ UpdateObject: Takes the ObjectKey of an object and the property/value pairs to update to the data store. This is more
optimized than the Persist call because the entire object is not required to be serialized to the DataServiceLayer to do
perform the action.
■ DeleteObject: Takes the ObjectKey of an object to delete from the data store. Like Update, this does not require
serializing the entire object.

Based upon the actions performed through the DataServiceLayer, the appropriate notifications are sent throughout the
system as described above. The calls to the DataServiceLayer can also be made through the ClientServerConnection
class as described above.

FindCollection/FindComplexCollection methods on the DataServiceLayer, a string formatted “WhereClause” can be


passed in. This is an OPATH formatted WhereClause that allows for abstraction between developer and data store.
Basically the developer codes to the Objects/Properties, and the DataServiceLayer relates the objects to the
Tables/Columns. This allows for the developer to write consumer code without any knowledge of what the actual data
store type is. The OPATH grammar is detailed in the code documentation and is straight forward.
DataServiceCollection allSampleObjects =
ClientServerConnection.Instance.FindCollection(typeof(SampleObject), null, null) as
DataServiceCollection;
foreach (SampleObject sampleObject in allSampleObjects)
{
//Do Something
}

There is an enumerator on the DataServiceCollection that returns a DataServiceObject derived instance when the indexer
is called. Passing in Null, Null on the FindCollection call requests that all objects of the passed in type be returned. If you
wanted to filter the results pass in an OPATH where clause:
DataServiceCollection someSampleObjects =
ClientServerConnection.Instance.FindCollection(typeof(SampleObject),
"SampleTable.Name = ?”, new object[]{“MyObject”}) as
DataServiceCollection;

The above call will only return objects that have property “Name” equal to “MyObject”. The same call can be made to Find,
which will return exactly one object matching the criteria.

DataServiceObject

DataServiceObject is the base class that all stateful persistable/requestable objects in the C•CURE 9000 system need to
derive from. All storage and state is handled in the base class, so that the derived classes need not be bothered by these
requirements.
A DataServiceObject has a trackable state that can be used to track its status during different operations. The state of
any DataServiceObject derived object can be retrieved by calling the State property of the object. This returns an
ObjectState enum detailed below:
■ Unknown: Default value.
■ Unchanged: This is the value of an object that has been retrieved from the database and has not been modified.
■ New: When an object has been created and not yet saved.
■ Modified: An object that has been retrieved from the database and been modified in its current instance.
■ MarkedForDeletion: In order to delete an object in the C•CURE 9000 system, the object has its MarkForDeletion
property set to TRUE, and then is persisted. So this state signifies that MarkForDeletion has been set.
■ NewMarkedForDeletion: This is the same as MarkedForDeletion above, except that this object’s state was New
when MarkedForDeletion was set to TRUE. This allows the underlying subsystem to optimize this object out prior to
serialization. The DataServiceObject has several Virtual methods/properties that can be overridden in a derived class
to perform custom operations. They are as follows:
■ Initialize: Initialize is called when an object is instantiated on the server and populated with data from the database.
This allows for custom initialization for each object prior to use.
■ Validate: Validate is called prior to an object being persisted to the database. This allows for any custom validation to
be performed and for the persist operation to be rolled back if validation is failed.
■ Delete: Called prior to an object being deleted from the database. Allows for custom operations to be performed to
perhaps delete other objects, fix up links, etc.
■ PostSave: PostSave, like the name implies, is called after an object has been persisted to the database and the
transaction committed.
■ PostDelete: Like PostSave, is called after a deletion is performed.

The DataServiceObject also contains a property indexer to return property values based on the string name of the
property. The property indexer is useful for doing runtime interrogation of an objects property structure.

Property changes to the DataServiceObject are recorded using a PropertyChangedEventArgs structure. The
PropertyChangedEventArgs contains certain properties that help in figuring out what changed, and what it used to be set
to. When a DataServiceObject’s state is Modified, the caller can call ChangedProperties on the derived object and
retrieve a PropertyChangedEventArgs array corresponding to the properties that have been changed since this object was
Unchanged.

Every DataServiceObject owns an AggregateCollection, which is by definition an IList of DataServiceObjects. This


allows for derived objects to have a hierarchical ownership object model. Objects in the AggregateContainer are
processed recursively by the DataServiceLayer and can be any combination of state. Every DataServiceObject has a
property called Aggregate that when it is in another objects AggregateCollection, will point to the owner object.

DataServiceObject also contains a generic Tag property, that allows for the storing of custom values that can be used in
validation or on initialization.

DataServiceCollections

When requesting data from the server, there are several options you can specify on the call that will determine what and
how much data is returned. The key to specifying this is using the CollectionCriteria object that is part of the SDK. The
CollectionCriteria object contains the following properties:
■ TypeFullName: The full name type of objects requested. This can be the full name of the type, or the full name of an
interface that multiple types that reside in the same table derive from.
■ WhereClause: This is an OPATH formatted where clause that can be used to filter data requested from the server.
■ The following formats are supported by the SDK's parser ([] brackets are only used to separate items in these
examples. They are not used in the actual programmatic where clause)
■ “Type.Property [!,=,<,>] ?”
■ “Type.Property LIKE ?”
■ “Type.Property BETWEEN ? AND ?”
■ “Type.Property IS [NOT] NULL”
■ Arguments: An object[] of arguments that correlate to the “?” in the where clause detailed above.
■ Properties: An array of strings corresponding to properties that are to be returned in the collection. This is a
performance optimization. If for instance, the only property the caller cares about is “Enabled”, then they can
requested only enabled to be serialized back to them for each object returned.
■ CollectionOptions: This is an enum of several additional options available. They are:
■ PopulatePropertyCacheFields: Instructs the server to populate property data for properties that are adorned with a
PropertyCacheEnabledAttribute. See the Attributes on Page 47 section for more information.
■ PopulateLookupFields: Instructs the server to populate internal structures corresponding to the
LookupPropertyAttributes on the class. See the Attributes section for more information.
■ ExactColumnReturn: If requesting certain properties in the original FindCollection call, the server will automatically
include properties that the requested properties rely on. This behavior can be overridden by specifying this option and
only the requested columns will be returned. (Note: Primary Key columns are always returned)
■ AsynchronousPaging: The DataServiceCollection object is responsible for paging data from the server to the client
when a request is made outside the process space of the server. The default behavior is for the DataServiceCollecion
to block until all the data is paged from the server to the client. By specifying this option the DataServiceCollection
will return control to the caller as it pages in the background so that the caller can pull data as it is paged.
■ TranslateEnumValues: Instucts the server to translate enum values into localized string values so that the data is
appropriately formatted for display. This requires that the type corresponding to the FindCollection call has matching
entries in the resource file for the enum values.
■ CountOnly: When this option is specified the server returns an empty DataServiceCollection with the collections
FullCollectionCount property set to the number of records in the database corresponding to the request.
■ RawPageFormat: When DataServiceCollections page data from server to client, the data is represented in raw object
[] format; one object[] for each row of data in the database. The underlying data store of a DataServiceCollection is an
ADO.Net Datatable, so when data is paged to the client it is converted from the object[] format to an ADO.Net
DataRow. By specifying this option the data is kept in its raw format and can be retrieved in this format by calling
RetrieveCurrentPagesAndRemove on the collection.
■ ReturnRowLimit: This is an integer value that specifies the maximum number of rows to return in the collection. When
combined with Sort this can be useful when the caller only cares about the last (n) rows.
■ Sort: This is a string value that corresponds to a standard sort syntax request. The sort can be made on any property
being requested and ascending/descending can be specified by “Name ASC” or “Name DESC” where Name is a
property in the type of collection being requested.

Attributes

There are several attributes that are used by the server to instruct the server how to process/format property data. The
premise of the C•CURE 9000 SDK is that the implementer using the SDK writes all code to the objects and not to the
database. In order to accomplish this, the server must be instructed how to map object properties to database storage.
The following attributes are defined for use in the C•CURE 9000 system:
■ PersistentClassAttribute: This attribute is automatically added to the persistent side of the partial class created by
the ClassBuilder application. This instructs the DataServiceLayer as to which database table this object resides in.
■ PersistentPropertyAttribute: This attribute is added to each persistent class property that maps to a column in the
database. Various parameters exist in this attribute to instruct the DataServiceLayer on how to represent this property
when retrieving and storing the values associated with it.
■ PropertyCacheEnabledAttribute: This attribute is placed on properties that reside in the custom side of the partial
class of the derived DataServiceObject. This attribute instructs the DataServiceLayer to allocate storage in the
underlying data structure of the object for the values of these properties. This allows for custom side properties to
appear to the consumer as if they were retrieved from the database, without needing persistent storage. Properties
with this attribute behave the exact same way as persistent properties when it comes to Property Changed
arguments and server notifications.
■ LookupIDAttribute: This class level attribute is placed in the custom side of the derived class, and is used to specify
that a certain property in the object is actually a link to another property in a different class and the DataServiceLayer
should replace this property with the value of the other. So for example, the first class has a property called PersonID,
and this value is a link to the Person table that contains a value called Name. Specifying this attribute will instruct the
DataServiceLayer to replace the column in the underlying DataTable with a column to hold the Name values from the
Person table, and populate each row with those values.
■ ForceDirtyOnAggregate: By specifying this class level attribute, the DataServiceObject that owns an
AggregateCollection of DataServiceObjects will have its state transitioned from Unchanged to Modified when objects
in the AggregateCollection are modified. The default behavior of a DataServiceObject is that the scope of its state is
only within the scope of the object. So specifying this attribute will allow an objects state to be modified based on
outside influences.
Create a client component to display in the Administration workstation
or Monitoring station

Client components
When the CrossFire platform is installed there is no executable to run; it consists of a set of data linked libraries (dlls) that
contain .NET classes which can be used to create a client application.

Client components framework


Client components are wrappers to user interfaces that encapsulate a particular set of implementation. Implementers
must derive a class from SoftwareHouse.CrossFire.Client.Core.ClientComponentBase to create a client component
class that can interact with the framework. Once the derivation is done the ClientComponentsManager will recognize the
class and load an instance of it at startup.

The main goal for an implementer shall be to encapsulate a particular data service object (or server object) and implement
all the client side logic that interacts with this kind of object inside the client component.
■ Client Components Base Class on Page 49
■ Client Component Attribute on Page 52
■ IClientComponent Interface on Page 52
■ Form Template
■ Executors
■ Application Layout
■ Activity Viewer
■ Control Library
■ Options and Tools
■ Linking to On-line Help

Client components base class


Client components are based on the ClientComponentBase class. This class gives implementers all the support and
features needed to integrate client side objects with the framework. There are not many methods that need to be
overridden, but there are some that should be mentioned:

IExecutor GetRunTimeDetailsExecutor()

This method returns the executor to invoke if client component details are supported. Details are not required but are used
by the activity viewer client component as follows:
Figure 9:

bool LoadComponent()

This method is called when the component is loaded for the first time by the ClientComponentsManager. If the method
returns false, the client component will not be loaded into the framework.

void RegisterExecutors()

This method is called when executors are to be registered for the client component. Implementers shall use this override
to register (or un register in the case of the default executors) custom executors. The following example shows how to
register custom executor classes.

public override void RegisterExecutors()


{
// Call the base class so all the defaults are registered. base.RegisterExecutors
();
// There is a class called ActivateEvent that implements
// IExecutor that is registered by the following line.
// Executors are registered by a string, it is recommended
// to use the full name of the class type so it is easy to
// look up and invoke later. Executors.Add(typeof(ActivateEvent).FullName,
new ActivateEvent());
. . .
}

Image StateImage (int stateEnumValue, bool defaults)

This method is called every time a user interface needs a state image for a particular state. Implementers shall override
this method if they would like to support features that require graphic representation of object states (ex: Maps). The idea
is to have the client components have the logic of returning the correct image for a particular state for a particular data
service object type (the encapsulated type).
Implementers may want to ignore the stateEnumValue argument and use the DataObject list instead to interrogate the
objects themselves for their current state. This argument exists since in some cases it is needed when there is no data
object to interrogate. Refer to the Monitor State attribute section for a detail description on how to set up a property of a
data service object to be an object state.

The defaults argument is used when custom state images are supported (refer to the Common pages section). State
images may be overridden by the user to allow customization so the overrides need to keep this into consideration. Using
this argument, the logic will know if it shall return the default images or the customized versions. The default images are
recommended to be stored in the resource file of the assembly in which the client component resides.

The following is an example of what an override which supports all the features mentioned above may look like:

public override Image StateImage(int stateEnumValue, bool defaults)


{
Image image = null;
if (defaults == false)
{
// First we get the actual state we are looking for if there is
// an object to get it from. In this case the state value passed
// in does not play since the object should know its own state.
// In the case there is no object then the caller
// must be looking for an image for a particular state. if (DataObjects.Count == 1)
{
// This must be an event since is an event client
// component.
XFEvent xfEvent = DataObjects[0] as XFEvent; if (xfEvent != null)
stateEnumValue = (int)xfEvent.MonitorState;
}
// See if we have a custom image we must return. The base class
// has the logic to look in the database and find any
// customizations made to the images by the user. image = base.StateImage
(stateEnumValue, defaults); if (image != null) return image;
}
// No custom image found so we will just return the defaults. if (image == null ||
defaults == true)
{
// Depending on the state we return the correct image;
EventMonitorState monitorState = (EventMonitorState)stateEnumValue;
if (monitorState == EventMonitorState.Unknown) return Resources.EventStateOffline1;
if (monitorState == EventMonitorState.ActiveAcknowledged) return
Resources.EventStateActiveNoAcknowlegement1;
if (monitorState == EventMonitorState.Armed) return Resources.EventStateInactive1;
. . .
}
// We could not find anything. return null;
}

Image StateImage (string stateCode)

This method is used the same way as its overload with the difference of the argument being a string and not an
enumeration type. Note that this method will be called first if overridden.

void BuildMenu (ToolStrip menu)

This method is called when a context menu is to be created. This may happen either by a user right clicking on a user
interface that supports context menus or by code requiring a menu. The base class will create the menu first (refer to the
executors section on which menus are created by default) and then is handed down to the overrides. Implementers have
the opportunity to modify the menu before is displayed by overriding this method.

Client Component Attribute


Now that you have a derived class from SoftwareHouse.CrossFire.Client.Core.ClientComponentBase, you must use the
SoftwareHouse.CrossFire.Client.Core.ClientComponentAttribute to link it to the framework. This attribute is what tells
the ClientComponentsManager that this client component needs to be loaded into the framework at runtime.

IClientComponent Interface
Interface allowing implementers access to instances of ClientComponentBase derived classes in a generic way. This
interface shall be used anywhere client component interaction is needed. Throughout the following sections you will see
that the base class is never referenced directly, so you should get familiar with the methods and properties exposed by
this interface.

Form Template
The form template is a windows form (System.Windows.Form) that hosts user interfaces created by implementers of
client components. These user interfaces must implement IClientComponentControlBuilder in order for the form template
to be able to host it and be able to interact with it. The Form template looks like the following (note the area in which user
controls are hosted).
Figure 10:

The form template may be customizable at a certain extent by IClientComponentControlBuilder implementations. These
customizations are done via the ClientComponentFormBehaviour class.

Object editors

Object editors are user interfaces that allow users to configure a particular object in the system. Implementers shall create
user controls that support the configuration of a certain type of object.

These interfaces are exposed via the ClientComponentEditUI attribute in the following way:
[ClientComponentEditUI]
public Control ClientComponentEditUI
{
get
{
// The user control that is hosted by the form
// template.
return new EventEditControl();
}
}

Object Runtime User Interfaces

Implementers also have the option to have user interfaces that do not configure objects of a respective type but have a
runtime graphical representation of the object types. For example, a map is configured or built via the object editor but the
maps object viewer displays the map in a run time mode, which makes the map read only for configuration changes.

These interfaces follow the same paradigm as the object editors with the difference that they are exposed via the
ClientComponentViewUI attribute instead of the ClientComponentEditUI attribute.
Implementers should use the ClientComponentFormBehaviour object to set up the form template in a way that the user
has no ability to edit configuration data. What is recommended is that the toolbar and the default controls (name,
description, and enabled) are hidden.

Common Pages

Common pages are user controls that may be shared by object editors. Object editors that follow the tab control paradigm
to group configuration items together may benefit from using common pages that can be shared by all object editors.

Common pages are exposed via the ISupportedCommonTabs interface. This interface lets the Form template know
which common tabs implementers would like to include with their object editors. The following are the common pages
supported by the framework:

State Images

The state images page is used to allow the user to customize the images for object states when applicable. Here is an
example of how to add this page to an object editor:
public partial class EventEditControl : UserControl,
IClientComponentControlBuilder, ISupportedCommonTabs
{
. . .
public ICommonTab[] CommonTabs
{
// Add all the instances of the common pages that the
// editor shall support.
get { return new ICommonTab[] { new StateImagesPage() }; }
}
public TabControl CrossfireControlTabControl
{
// Editors using the common pages must add them to a
// certain tab control. The interface needs which tab
// control to add the page to. get { return this.TabControl; }
}
}

Executors
Client components that encapsulate particular data objects may also expose functionality via classes that implement the
IExecutor interface. An executor is a class that encapsulates certain functionality or feature that targets a particular data
object type.
The ClientComponentBase class allows an override to register executors with the ClientComponentsManager. These
overrides can do two things: register custom executor classes and manipulate default executors that are registered by the
base class.

The following are the default executors that can be used or not used by derived classes:

Edit: The edit executor will display the form template hosting the object editor user control. Object editors are exposed via
the ClientComponetEditUI attribute.

View: The view executor is a broadcast delegate and allows user interfaces to handle the event and display object viewer
user control in their own context. This is used inside the ConfigurationControl to display the user interface inside a tab.
(ex: maps). The actual user interface used is exposed via the ClientComponetViewUI attribute.

View in Current Tab: Same as the view executor but the view user interface replaces the current tab in the
ConfigurationControl.

Popup View: The view executor will display the form template hosting the object viewer user control.

Delete: The delete executor will display a confirmation dialog to the user and will delete all the objects that are currently in
the DataObjectKeys list of the client component.

New: The new executor will create a new data service object and then call the edit executor for the user to configure the
newly created object.

New From Template: The new from template executor will create a new data service object from a template and then
call the edit executor for the user to configure the newly created object. The template used is stored in the DataObjects
list of the client component. The framework will only look at the first item of this list since this executor only works one
template at a time.

Add to Group: The add to group executor will prompt the user to select a group and then adds the objects in the
DataObjectKeys list to that group.

Property Set: The property set executor will prompt the user to select a property and a value. It then uses the object in the
DataObjectKeys list and sets the selected property to the entered value of all the objects in that list. Note that depending
on what types of objects are in the list, the list of valid properties is generated. This executor only works with
homogeneous types of lists.

The above executors are all registered by the base class and can be unregistered by removing the items from the
Executors list in the RegisterExecutors override. The following is an example of how to achieve this:
public override void RegisterExecutors()
{
base.RegisterExecutors();
// Remove the ones we don't want. The DefaultVerbs enumeration
// contains all the default executors.
Executors.Remove(DefaultVerbs.PopupView); Executors.Remove
(DefaultVerbs.ViewCurrentTab); Executors.Remove(DefaultVerbs.View);
. . .
}
ICustom Executors

Implementers may also implement their own custom executors. As said before, these classes are implementations of the
IExecutor interface and are registered via client component classes. The registration of these executor classes is very
important because that is the way the framework accesses them and is able to invoke them without knowing too much
about the internals of them. It is recommended to use the full name of the executor’s type for this (GetType().FullName).

Context Menus

Context menus play a big role in the client framework and are usually the gateway into executors in any given system. All
the executors that are registered will be incorporated in the context menu unless implementers return false from the
ShowOnContextMenu property.

Note there is another interface called IPrompExecutor which derives from IExecutor that is used for executors that prompt
the user for information before executing. This is used by the context menu so an ellipsis (…) is placed at the end of the
menu item to indicate to the user that clicking on the menu does not have an immediate action.

Application Layout
The application layout may be extended by writing a custom “viewer” user interface or by extending the object viewer
client component.

Viewer

A viewer is a user interface that extends the application layout, allowing implementers to design and implement mini
applications that can be integrated easily.

Viewers have two sides to them, the client side and the server side. The client side is the code that deals with strictly the
client component and the user interface for the viewer itself. The server side deals with the saving of the viewer into the
database.

Client Side

To add a viewer to the application layout, the client component must:


1. Register itself with a ComponentType equal “Viewer”. A viewers ClientComponentAttribute for a viewer may look like
the following:
[ClientComponent(typeof(SoftwareHouse.CrossFire.Common.Objects. ActivityViewer),
"Viewer",
IconResourceName = "ActivityViewer", DisplayNameResourceName =
"ActivityViewerDisplayName")]

2. Implement the ILayoutInfo interface.


The following is the application layout editor showing all the viewers it discovered at the time it was invoked. Once a
client component registers itself as a viewer, the application layout will look for two properties of it to be able to add it
to the list of available viewers.
— The first property is the DisplayName property (this is also the DisplayNameResourceName of the client
components attribute) which is used to show the user the name of the viewer (ex: activity viewer).,
— The second item is a large image to add to the left side of the list item. For the later, the ImageListLarge is
used to extract the image.
Figure 11:

Implementers must implement the ILayoutInfo interface which will describe the viewer’s behavior once it has been
dropped. This interface allows implementers to customize the following:
— The tooltip to display in the pane the viewer is dropped into.
Figure 12:

— The icon to display in the pane the viewer is dropped into.


Figure 13:

— The title to display in the pane the viewer is dropped into.


Figure 14:

The following is a sample of an implementation of this interface:


[ClientComponent(typeof(SoftwareHouse.CrossFire.Common.Objects.ActivityViewer), "Viewer",
IconResourceName = "ActivityViewer", DisplayNameResourceName = "ActivityViewerDisplayName")]
class ActivityViewerClientComponent : ClientComponentBase, ILayoutInfo
{
. . .
#region ILayoutInfo Members
public string PaneTitle
{
get { return DisplayName; }
}
public Image PaneImage
{
get { return ImageListLarge.Images[ComponentType]; }
}
public string PaneTooltip
{
get { return Resources.ActivityViewerToolTip; }
}
#endregion ILayoutInfo Members
}

Server Side

On the persistence side (make sure you are familiar with how the server objects are laid out in the CrossFire framework),
two properties must be supplied:
■ ApplicationLayoutID (int). – This is the id of the application layout the viewer instance is dropped on.
■ Identifier (string). – This property is used by the application layout editor to save the position of the viewer inside the
panes.

One custom side property must be supplied:


■ ApplicationLayout (IApplicationLayout). - Returns the application layout object the viewer instance is dropped in. Here
is what this property shall look like:
public IApplicationLayout ApplicationLayout
{
get
{
// Buffer the application layout object so we don’t
// call find all the time.
if (_applicationLayout != null) return _applicationLayout;
if (ApplicationLayoutID == int.MinValue) return null;

// Find the object.


return ClientServerConnection.Instance.FindObject
(typeof(SoftwareHouse.CrossFire.Common.Objects.ApplicationLayout),
ApplicationLayoutID, true) as IApplicationLayout;
}
set
{
_applicationLayout = value;
}
}

One override method must be supplied:

The object must initialize its container with the link objects which link the viewer and the application layout that the viewer
belongs to. Implementers do not have to worry about the ViewerLink since it is part of the framework. The following is
what this method should look like:

protected override void Initialize(bool loadContainer)


{
if (loadContainer)
{
if (ObjectKey == null) return;

IDataServiceCollection viewerLinks =
ClientServerConnection.Instance.FindCollection (typeof(ViewerLink),
"ViewerID=?",
new object[] { ObjectKey.Values[0] }) as IDataServiceCollection;

foreach (IDataServiceObject dataObject in viewerLinks)


{
dataObject.Container.Add(dataObject);
}
viewerLinks.Dispose();
}
}

Object Viewer

The object viewer is a multi purpose viewer that can be used in the application layout to display client components “view”
user interfaces in a generic way. When you drop an object viewer into an application layout the editor will interrogate all
the client components currently registered for the ones that contain a valid “view” user interface. Remember that the
ClientComponentViewUI attribute is used to mark a user interface in a client component as is “view” user interface.

The following is what the object viewer’s editor looks like:


Figure 15:

When the user clicks on the type selection control, all the client components are enumerated and the ones that contain
“views” will be included in the valid list. In addition, the client component has to encapsulate a data service object,
because the object viewer must save which object the user would like to see at runtime. For example, if you have a map
called “first floor”, because the map client component encapsulates the map data service object, and it contains a “view”
user interface (the actual map of the first floor), then it can be picked in the object viewer. Once picked and the application
layout is opened, the map of the first floor is opened in the pane in which the object viewer was dropped.
To allow the user to have a feeling of what the user interface will look like at runtime, implementers can use the
ClientComponentStaticViewUI attribute that can be used to return a read only version of the view user interface.
Implementers should not allow the interface returned to be editable or allow the user to interact with it in a meaningful way.

Activity Viewer
The activity viewer will display all the messages that are journaled by the system. Please refer to Journal Messaging on
Page 85 for information and samples of how to journal messages into the system.

Control Library
The control library is a collection of controls , some derived from Microsoft controls and some completely new controls,
whose purpose is to simplify the application code needed to implement forms which appear somewhere in a CrossFire
application. The controls in the control library will, when properly initialized, set the form error provider, set tooltips for the
control based on the attributes of the object bound and interface with the Data Service Layer for object changes, as well
as loading and saving properties of the object.

The control library consists of at least the following controls:

■ CheckBox ■ DomainUpDown ■ PropertyValueSelector


■ CheckedListBox ■ GroupBox ■ RadioButton
■ ComboBox ■ ListBox ■ SelectionControl
■ DataGrid ■ MaskedTextBox ■ TextBox
■ DataGridView ■ NumericUpDown ■ TrackBar
■ DateTimePicker ■ Panel ■ TreeView

These controls are comprised predominantly of the controls written by Microsoft with a wrapper around them to make
interface to the Data Service Layer easier. One control in particular is specific to the CrossFire system. This is the
selection control which will allow the user to select a Font, Color, Directory and Property of a type or instance of a type.

The control library is primarily useful in forms which are hosted via the ClientComponentFramework as all of the work
needed to initialize the controls is contained in this module. When created in this way in some cases a GUI may be
created without any hand written code at all. The framework will create a form and insert code initializing the controls as
well as getting the object to be manipulated to the controls. The Editor / View programmer should try to bind as many of
the controls to the appropriate properties of the object type to be edited / viewed. In this way you will get the maximum
benefit from the Control Library while minimizing the amount of code to be written.

All of the controls use reflection to find attributes of the objects which they are bound to. These attributes will inform the
control what to place in the tooltip of the given property, as well as the “What’s This?” help text to display for this property.

ICrossFireUserControl

All of the CrossFire controls implement ICrossFireUserContol which defines an interface which is discovered by the
ClientComponentFramework. The purpose of this interface is to allow an exchange of state and information between the
framework and the controls, thus easing the task of the application programmer when making Client Components which
will be hosted in a ClientComponentFramework based system such as C•CURE 9000. In this environment, an editor may
simply bind controls to the properties of the object and need not concern itself with loading and saving information to the
controls on the screen. Even tooltips will be set from the object which is bound. This does not replace complicated code
needed for some custom controls, but generally simple data types can be edited and displayed by simply binding the
controls to the properties of the object.
The calling sequence is somewhat interesting if the application has a requirement which cannot be met via the normal
state flow of the framework. In order to create a form which has controls on it which are initialized at some arbitrary point
the following must be done:
1. Set the DataObject on each of the controls
2. Call Initialized () to setup the controls and get them going.

There is a coding shortcut for some forms. If you place all of these controls in our GroupBox and call Initialized on the
GroupBox, the GroupBox will call initialized on all of the child controls. This is also true of the DataObject property on the
GroupBox. This is a simple way to initialize all of the controls at once with very little code. Generally as the GroupBox will
be in the Controls collection of the parent form, the Controls in it will function normally after this. When the form closes,
the framework will call Save on each of the controls in the tree of Controls containers from the form on down. If the
application needs to force the controls created this way to save at some other point it is necessary to call Save on each
control.

Binding

Generally it will make for simpler code in the client component if the properties of the object can be bound to the control.
The code in the controls generally knows how to retrieve information from the class and property which is bound to it. This
includes retrieving enum values and translating them into the language of the client. Bound properties will be interrogated
via reflection to discover attributes which describe them, such as limit information and help/tooltip settings. This
information will need to be supplied by the application code for controls which are not bound.

Status Mode

Most of the controls can be placed into a “Status” mode where they will display the current value of the property of the
object in question. This is done by setting the control as read-only and setting LiveData to true. The reasoning behind this
is that there can be problems when the properties of the object are allowed to be changed by the system and also by the
user. It could become impossible, for instance, to change the object to some known state. If the application requires both
editing and change events to change the control, the application code will have to handle events and change the control it
self.

Attributes

The attributes recognized by the Control library are described in the following table. Not all controls will use any particular
attribute and some of the attributes are used in the GUI in general like the DynamicViewAttribute. The control library and
other portions of the GUI will, via reflection, look for some or all of the following attributes and read from their values which
will be used to configure various aspects of the control. For example, the ToolTipText attribute will return the tooltip which
should be assigned to each control that is bound to the property in question. Usually these attributes will be emitted by the
ClassBuilder automatically based on user input when designing the classes.

To save space in the following table, the attribute usage form of the attribute name is displayed but the class names all
have the trailing “Attribute” so the DymanicViewAttribute is listed in the table as DynamicView.

Attribute Description

DynamicView This attribute will set type of column to display in the DynamicView.
Attribute Description

HelpProviderText This attribute will set the Help provider info for this control.

ToolTipText This attribute will set the tool tip for the control associated with the property this attribute is specified for.

MaskTextBoxFormat This attribute will set the mask in a maskedtextbox if it is not set in the designer.

EnumerationItem This attribute will describe the values to be placed in a combo box or listbox for a property which will be
represented in the list / combo box.

ClosedEnumeration Properties marked with this attribute will not allow anything other than the enumeration to be specified in the
control.

ControlIntegerRangeValidation Properties marked with this attribute will not allow anything out of the range of the values specified to be
entered in to the control.

UserSelectable Classes marked with this attribute will be displayed in the selection control when a selection control is displayed
that displays classes. All of the classes which don't have this attribute will not be displayed in the list box for
selection.
This attribute on a property will cause the controls to force the user to only select one of the available
enumerations and not be able to type something in themselves.

EnableIme Properties marked with this attribute will allow the IME to come up for this property in the control.

DisableIme Properties marked with this attribute will not allow the IME to come up for this property in the control.

CultureSpecific This attribute specifies that the class it is attached to has culture-specific properties in it and the user of the
class in question must set the culture attribute on the class before using the properties.

ClassDescription This attribute describes the default value which should be placed in the control when no other valid value can
be determined.
The resources for this attribute will be contained in the assembly which hosts the class to which the attribute is
applied. In order to allow for this, the type of the class must be passed in to the constructor for the attribute.

DisableIme Properties marked with this attribute will not allow the IME to come up for this property in the control.

CultureSpecific This attribute specifies that the class it is attached to has culture-specific properties in it and the user of the
class in question must set the culture attribute on the class before using the properties.

ClassDescription This attribute describes the default value which should be placed in the control when no other valid value can
be determined.
The resources for this attribute will be contained in the assembly which hosts the class to which the attribute is
applied. In order to allow for this, the type of the class must be passed in to the constructor for the attribute.

DataGridView and Special Mode

The DataGridView control will act as a standard DataGridView control, or it may be supplied with a list of
SelectionColumns(a class in shared) which will describe for it what to display in the grid. When used in this way the grid
can edit objects as well as create them and delete them. Generally this has been used to implement grids which allow the
description of boards inserted into a system.
Validation

All of the controls will validate based on the rules supplied in the attributes. Things like the range of integer numbers etc.
can be completely validated by the control itself with no extra coding from the application. In the cases where this rather
simple validation is not enough, there is an OnLocalValidation event which may be wired up for the application to do its
own validation. When this event is hooked up it will replace all of the internal validation.

Resource Placement

In order for the system in general to discover the resources for objects, property names and class names, there need to be
some rules as to where they are located and what the ids will be. The resources generally will be located in the assembly
which owns the type. This means that the resource must be located in the same assembly as the code for the object.

Class names, property names and translatable DynamicView data are to be stored with the name of the item.
Enumeration values are stored as <PropertyName>_<Enumeration value> e.g. StatusProperty_Stopped. If a resource is
looked up and cannot be found it is displayed in the GUI as *** <Resource ID> ***. This should facilitate discovering and
creating the resources necessary.

PrintersHelper

The PrintersHelper class encapsulates simple printing. The object will format and print pages in a more “Word” like format.
Pages are numbered and the text can be placed as needed. This class is sufficient to print the results of operations etc.
but is probably not sophisticated enough to actually printout complete documents or reports.

Specialty Controls

The following controls are somewhat special in the CrossFire system. They either enhance already existing functionality
(TextBox, ComboBox) in special ways or they perform completely new functions in the CrossFire system
(SelectionControl, MultipleErrorDialog)

SelectionControl
Figure 16: The Control
Figure 17: The Selection Form

The selection control is designed to allow a unified way to pick things in the system. Pressing the “…” will bring up a
selection of whatever is configured for the control. The control can bring up a list of Colors, Fonts, Directories, Files, types
(DataServiceObjects), properties of a type and objects/groups of objects of a specific type. The control can take a where
clause to allow filtering the list and will accept a list of SelectionColumns which will describe the columns of the object to
display. There is an indexer which will allow the application to get the list of selected objects, as well as a type converter
which will allow many various types of properties to be bound to the selected information. There are event handlers to
allow the application to sync with the pressing of the button as well as the ultimate selection of objects. The toolbar can be
configured to display or not and some of the buttons can be removed. Data can be filtered by entering the filtering
information and data can be grouped by dragging a column into the grouping section.

ComboBox

The ComboBox, when bound to a property which is an enumeration, will fill the items list with the translated strings which
describe the values of the enumeration bound to. When the user selects one of the values, the correct enumerated value
is set to the property which is bound. This ComboBox also has the ability to size the drop-down to the contents of it, unlike
the Microsoft control which is fixed. This translation of enumerated values only happens when the control is bound to a
property. If the control is hand loaded the application will be responsible for all of this.

MultipleErrorDialog

The MultipleErrorDialog is designed to allow portions of the application to display status and error information to the user.
Generally it displays the information as a tree with the idea being that as the user expands the tree they get ever more
detailed information. The dialog has the ability to print the display as well as email it. There is a progress bar which can be
programmed as needed. The interface to the MultipleErrorDialog is basically that of a TreeView the application
programmer inserts nodes into the tree.

TextBox

Other than the standard functionality, the TextBox will also display enumerated values translated as the combobox does.
Also it will fill the auto complete list for the control enabling AutoComplete functionality if that is enabled. The TextBox
lastly can be placed into a status mode by setting it to ReadOnly and also setting LiveData to true. In this mode it will
display changes in the property it is bound to but will not allow the user to change the value.
Options and Tools
Client components only play a role when providing privileges to the options and tools item. Implementers of options and
tools should use the “SYSTEM_TOOLS_CLIENT_COMPONENT” prefix when registering the client component. This will
distinguish the client component class in question with the others. The following is an example of an options and tools
client component registration:
[ClientComponent("SYSTEM_TOOLS_CLIENT_COMPONENT_ImportExportHistory"
, "ImportExportHistory", DefaultVerb = DefaultVerbs.Edit, IconResourceName =
"ExportImportHistory")]

public class ImportExportHistoryClientComponent : ClientComponentBase


{
. . .
}

Linking to On-line Help


C•CURE 9000 uses Microsoft Compiled HTML Help format to display on-line help.

The Client Component Framework supports the ability to define a helplink from each client component that a user can
access by pressing F1(function key 1). This control uses the HelpProvider component to locate and display a help topic
inside a Compiled HTML Help file (with a .chm extension).

When you use the Client Component Framework to generate your client program, the links to on-line help are defined in
the class that implements IClientComponentControlBuilder. For example, if you were creating an Activity Viewer Editor,
IClientComponentControlBuilder would be implemented as:
public partial class ActivityViewerEditor : UserControl, IClientComponentControlBuilder,

In this class, you would define the HelpNameSpace and the HelpKeyWord. HelpNameSpace is the location of your help
file relative to your client component.

Example:

"help\MainHelp.chm"

Points to the file MainHelp.chm in the \Help directory.

HelpKeyWord is the path within the .chm file to the topic you want to display

Example:

"Activity_Viewer.htm"

Points to the file Activity_Viewer.htm that is compiled into MainHelp.chm in the \Help directory

C•CURE 9000 uses a merged help file system, where MainHelp.chm is the master Help file, and its Table of Contents
includes merged help files (such as ApplicationLayout.chm).
To reference a merged help file, the HelpKeyword includes the merged help file name followed by "::/", then the file name
of the topic within the merged help file.

Example:

ApplicationLayout.chm::/Activity_Viewer.htm references the help topic Activity_Viewer.htm that is compiled into


ApplicationLayout.chm. ApplicatioLayout.chm is merged into the Table of Contents of MainHelp.chm.

You can view the help file path that the client component is calling if you open a Help topic and right click in the topic
viewer, then click Properties. Select the Address (URL) field on the Properties dialog and drag downward (the field
contents are larger than the visible area) to display the Help topic path.

Example:
mk:@MSITStore: C:\PROGRAM~1\SOFTWA~1\SWHSYS~1\Client\Help\ApplicationLayout.chm::/Activity_
Viewer.htm

The following code sample shows the HelpNameSpace and HelpKeyword definitions for the Activity Viewer topic in the
C•CURE 9000 Help System.
public partial class ActivityViewerEditor : UserControl, IClientComponentControlBuilder,
...
#region IClientComponentControlBuilder Members
/// <summary>
/// This property contains the namespace for the help provider. This property will be used
/// by the client framework to initialize the HelpNamespace of the HelpProvider prior to the
/// initialization of the client component.
/// </summary>
public string HelpNameSpace
{
get
{
return @"help\MainHelp.chm";
}
}
/// <summary>
/// This property will return the string which describes the help keyword(path) within the
/// help system that should be used to retrieve the help topic for this WHOLE component.
/// </summary>
public string HelpKeyWord
{
get
{
return @"ApplicationLayout.chm::/Activity_Viewer.htm";
}
}

Navigation Framework
The navigation framework allows the extensibility of the object navigation inside the C•CURE 9000 Administration client.
Implementers may embed their functionality into the main navigation mechanism the user uses to interact with most of
the objects in the system.

Configuration Control
There exists a user control called Configuration Control that looks like the following:
Figure 18:

It consists of a navigation area holding navigation panes and a container area that contains tabs which correspond to
different types of user interfaces. Implementers extending the C•CURE 9000 product do not have to worry about this
control since the Administration application consists of a form and this control docked filled into it.

The form has a title bar, a status bar, and a menu bar. In the middle we included the Configuration Control and we docked
it fill which means that it will take over the entire middle area. This form was later added to a .NET C# windows application
that created an executable that could be run from the windows explorer.

The following is what configuration control looks like hosted in the C•CURE 9000 Administration Application:
Figure 19:

Once the application is run:


Figure 20:

Notice that there are some navigation panes that were not there at design time: the configuration pane and the Data Views
panes. These panes get added at runtime by the Configuration Control(also notice the background image; if you would like
to customize this, set the TabAreaBackground property of the ConfigurationControl). The following sections explain how
to extend the navigation bar inside the this control.
Navigation Panes
Navigation panes are classes that describe a pane that is included in the navigation bar at runtime. Classes that
implement the INavigationPane or ICustomNavigationPane will be included in this category.

INavigationPane

Objects of this type add a navigation pane to the navigation bar that includes the following features:

New button to allow creation of objects and object templates.

Active type combo box specifying the current type the pane is working with. When this is changed, the search capabilities
are all scoped to this class of objects.
Figure 21:

This interface allows the following customizations:


■ Icon: The icon to show in the navigation bar.
■ Name: The text to display in the navigation bar.
■ Quick Find support: If enabled, the “quick” button shall show up for the pane. This button allows trivial search
capabilities. Refer to the Quick Find section of this document to learn how to customize this feature.
■ Advance Find support: If enabled, the “advanced” button shall show up for the pane. This button allows the use of
queries for more advanced searching.
■ Supported Types: The list of supported classes that will be navigated via the navigation pane. In addition, these
types are the ones that get added to the type’s combo box mentioned before.
■ Active Type: This shall be just a get/set of a member variable. The property will be used by the navigation bar.

Navigation Pane Code Sample


The following is a code sample of a complete navigation pane class:
public class ConfigurePane : INavigationPane
{
public ConfigurePane()
{}
#region INavBarPane members. public string PaneName
{

get { return Resources.ConfigurePaneName; }


}
public Image PaneIcon
{

get { return Resources.ConfigurePaneImage; }


}
public bool QuickFind
{
get { return true; }
}
public Type ActiveType
{

get { return _activeType; } set


{
_activeType = value;
}
}
public ICollection <Type> SupportedTypes
{
get
{
IList<Type> types = new List<Type>();
types.Add(typeof(TimeSpec));
types.Add(typeof(Privilege));
types.Add(typeof(XFEvent));
types.Add(typeof(Operator));
types.Add(typeof(Group));
}
}
#endregion
#region Private fields
private Type _activeType = typeof(XFEvent);
#endregion
}

ICustomNavigationPane

Objects of this type add a navigation pane to the navigation bar that includes all the features of the INavigationPane as
well as the following:

Custom user control to include inside the navigation pane. Using this interface you may include a user control for the
users to use to do custom navigation. The following is a snapshot of the hardware pane which uses this interface:
Figure 22:

Allow or disallow the new button to be enabled for a particular object type. This shall be used in the cases where the
custom control handles all the object creation.

Custom Navigation Pane Code Sample


The following is a code sample of a custom navigation pane class (Note that the code that implements INavigationBar is
not listed since it is not different from the previous example:
public class HardwarePane : ICustomNavigationPane, IDisposable
{
public HardwarePane()
{
_hardwareTreeView = new HardwareTreeView(this);
}
public Control ContainerControl
{
get { return _hardwareTreeView; }
}
// This is called by the navigation bar when the bar is invoked
// by the user the first time. Implementers shall keep track
// since usually these types of panes have controls that take
// some time to load and should be loaded only one time and then
// keep track of changes.
public void Initialize()
{
_hardwareTreeView.Initialize();
_isInitialized = true;
}
public bool IsInitialized()
{
return _isInitialized;
}
public bool NewButtonEnabledForType(Type type)
{
// The tree will take care of the creation of certain
// object types but not for hardware folders. Note that
// this is an example and implementers shall use the types
// listed in the SupportedTypes list.
return type == typeof(HardwareFolder);
}
public void UpdateNewButton(ToolStripSplitButton newButton)
{

// Now get the drop down for the new menu. The helper method will
// return the context menu for the selected node in the tree control.
ContextMenuStrip contextMenu
=_hardwareTreeView.BuildMenuForSelection(false, false);
newButton.DropDownItems.Clear();
if (contextMenu != null)
{
// Fill in the drop down menu. ToolStripItem[] toolStripItems = new
ToolStripItem[contextMenu.Items.Count];
contextMenu.Items.CopyTo(toolStripItems, 0);
newButton.DropDownItems.AddRange(toolStripItems);
// Wire the button. This is how you clear an even // handler belive it or not.
newButton.ButtonClick -= new
EventHandler(_hardwareTreeView.NewClick);
newButton.ButtonClick += new
EventHandler(_hardwareTreeView.NewClick);
}
}

Template Support in Context menus


Sometimes it is useful to create a context menu that includes all the templates for a desired class of objects. When this is
needed (like in the previous figure), a class called ComponentsHelper is used. This class has methods to generate
different kind of context menus that involve template items.

Options and Tools Pane


The options and tools pane can also be extended via the IOptionsAndToolsItem interface. This shall allow adding buttons
to the pane at runtime. The following is a snapshot of the Audit log pane item that is included in the :
Figure 23:

This interface allows the following customizations:


■ Icon to display
■ Text for the item.
■ Action when clicked.

An option and tool item can be hooked up to the operator privilege engine by creating a client component shell. Refer to the
client component (options and tools section) section of this document.

The following is a sample of an options and tools item.


class SampleOptionsAndTools : IOptionsAndToolsItem
{
#region IOptionsAndToolsItem Members
public Image DisplayIcon
{

get { return Resources.SampleImage; }


}
///<summary>
/// Method called when the user selects the link.
///</summary>
public void OptionClicked()
{
// Type in the code that executes when the user clicks on
// the link.
. . .
}
public string DisplayName
{
get{ return Resources.SampleDisplayName; }
}
///<summary>
/// Gets the type of client component if applicable. This is
/// used for operators and privileges.
///</summary>
public Type ClientComponentType
{
get { return typeof(EventPriorityClientComponent); }
}
#endregion
}

Search Support

Inside the navigation panes, implementers can turn on and off search capabilities for all the supported object classes that
the pane will support. There are two types of search mechanisms inside a navigation pane:
■ Quick find
■ Advanced search.

Advanced Search

Implementers will get this capability by turning QuickFind on. There is nothing else that must be done for this type of
search.

Quick Find

Quick find consists of a group of properties that the implementer decides to expose as most searched or searchable
criteria. The properties that are exposed to the user are reflected by the navigation framework using the
UserInterfaceProepertyAttribute residing in the SoftwareHouse.CrossFire.Common.Sharedassembly. This attribute will
tell the framework that the respective property shall participate in the quick find area. The following is an example of what
this looks like:
Figure 24:

QuickSearch can be turned on or off for a particular property. Additionally, you can indicate what default control shall be
used to query for the searchable value. The following is what the code should look like:
[UserInterfaceProperty(ControlType="SoftwareHouse.CrossFire.Client.
ClientComponentsFramework.ControlLibrary.TextBox", QuickSearch=true)] public virtual string
Name
{
. . .

The control type must be a control in the CrossFire control library since that is all that is supported by the
NOTE QuickFind control at this time. The benefits of this approach is that all the validation and control behaviors
are taken care of by the respective control, leaving implementers only with the task of picking which
properties are to be used. See Actions for more information.

Actions
Actions are very important for an event management system. Implementers may add their own client or server actions
using the framework. Actions are triggered by events, which make actions very powerful since these events may be
triggered by many conditions and may themselves invoke other actions. Actions can execute on the client or on the
server.

When an event is configured, the user has the ability to configure the actions the event is going to trigger during activation.
The following is a snapshot of where the actions are configured.
Figure 25:

Custom actions when implemented are added into this user interface. An action has two pieces:
■ The user interface used to configure the action
■ The object that is used when the action is triggered.

Note that implementers do not need to add any database support for custom actions. These action objects are serialized
into the database by the framework via the XML object serializer.
■ There are two interfaces that need to be studied when developing actions; IActionConfiguration is used for
configuration data for the action
■ IActionExecutor. is used to execute the action.

If an action is to be executed on the server (and this is also recommended for a client only action), the IActionExecutor
implementation shall reside in a common assembly so both the client and the server can reference it.

Client Side
On the client side, implementers must develop classes that implement the IActionComfiguration interface in the
Common.Interfaces assembly. This object shall describe the client side behavior of the action which is integrated into the
editor.

On the client, the action needs a user interface that allows users to configure the action. Since all actions are different, the
mentioned interface supports the standard System.Windows.Forms.Control class to allow implementers full control over
what the user interface looks like. This control will be placed inside the editor (action user interface area) when needed.

Note that implementations need to support IDisposable when applicable since the framework will dispose actions as it
needs to.

The following is a sample of a class which implements IActionConfiguration:


public class PlaySound : IActionConfiguration, IDisposable
{
#region Constructors
///<summary>
/// Default constructor.
///</summary> public PlaySound()
{
// The action object needs to know the type of object that
// configures the action on the client. This way the editor
// can create the correct action on the client. Use the
// ConfigurationType property to set this type.
_action.ConfigurationType = typeof(PlaySound).FullName;
}
end#region

#region IActionConfiguration Members


///<summary>
/// Gets/Sets the action object for the configuration editors.
///</summary>
///<value><see cref="IActionExecutor"/> action object.</value> public IActionExecutor
ActionObject
{
get { return _action as IActionExecutor; } set
{
// Client classes can keep the actual type of action
// since the class can be referenced from a common to
// the server and the client assembly.
_action = value as CommonActions.PlaySound;
}
}
///<summary>
/// Gets the sound file to use in the details.
///</summary>
public string Details
{
get { return _action.SoundFile; }
}
///<summary>
/// Create a new play sound control for the event editor to
///display.
///</summary>
///<value><see cref="PlaySoundControl"/>object with the play
///sound configuration.</value> publicControl Editor
{
get
{
if (_playSoundControl == null) _playSoundControl = newPlaySoundControl(_action);
return _playSoundControl;
}
}
///<summary>
/// Use this method to validate entries into the action editor.
/// The event editor will call the actions saving before saving
/// the event itself.
///</summary> publicbool Saving()
{
if(_playSoundControl !=null) return _playSoundControl.Saving();
else
return true;
}
#endregion
#region IDisposable Members
///<summary>
/// Need to dispose _playSoundControl.
///</summary>
public void Dispose()
{
if (_playSoundControl != null)
_playSoundControl.Dispose();
_playSoundControl = null;

GC.SuppressFinalize(this);
}
#endregion
#region Private fields.
private PlaySoundControl _playSoundControl; private Common.Actions.PlaySound _action = new
Common.Actions.PlaySound();
#endregion

Action Execution

Server Side

Actions on the server are executed asynchronously by design to avoid timing issues and process blocking. For an action
to be executed on the server, the proper IActionExecutor interface must be implemented.

There are two items that need to be implemented:


■ The Execute method is what is called when the action is triggered.
■ The ConfigurationType property string is used to store the string representation of the type of object that configures
the action on the client.

Implementers must get the full name of the type that implements IActionConfiguration and set this property in the
constructor of the action itself as follows:
[Serializable]
public class ActivateEvent : IActionExecutor, IDisposable
{
///<summary>
/// Constructor
///</summary>
publicActivateEvent()
{
// We need to fill in the configuration type so that
// the action can be translated at the client. ConfigurationType =
"SoftwareHouse.CrossFire.Client. Actions.ActivateEvent";
}
. . .
}

A string is used since the client assembly cannot be referenced in the common area in which the server actions are
implemented. If you remember, the client assembly has a reference to the common assembly already, so a backward
assembly is not allowed. To get around this problem, the type is stored as a string.

Implementers may add private members to their class implementation without having any database impact. This is due to
the framework’s use of the XML serializer to create these objects at runtime (observe the Serializable attribute
requirement on top of the class in the sample provided).

Client Side

On the client side, actions may be executed synchronously or asynchronously. Actions that are executed on the server or
on the client are implemented the same way. The IActionExecutor interface is used for both client and server.

Asynchronous actions must implement IAsyncActionExecutor instead to allow the framework to distinguish action
execution.

Maps
Figure 26: Maps in C•CURE 9000

Due to the major recoding of the maps in 2.80 for the C•CURE 9000, all integrations which expose icons added to the map
should undergo some re-testing of the maps.

Complete the following tests of the maps:


■ Verify that for each integration you can add each type of object that the integration creates. This applies only to
objects which are assignable on maps. Do this by editing a map and clicking the + button. Note, the objects are
combined somewhat differently than they are in 2.70. If the new object is derived from a native C•CURE 9000 object,
just select the C•CURE 9000 object type and then select the custom object of the integration. For example, if the
custom new object is a subtype of Input, first select the Input type, then the subtype can be discovered in the list that
appears. If an object that should be able to be put on a map cannot be added, then a bug has been discovered.
■ The new search bar on the left can be used to add new types. The type name of the custom integration object can be
entered but cannot be searched by name. For example, if DSC is entered, all types containing the word DSC display.
When you click one of the types, it displays a dynamic view from which the desired object can be dragged and
dropped onto the map.
■ Once a map has been designed containing icons representing at least one of each type of object supported by the
integration, save the map and then open it for viewing:
■ For each icon on the map, change the state and verify that the icon now displays with the correct state image. At
minimum, verify this against two states for each icon. Physical hardware is required for this verification.

Journal Messaging
You can implement custom Journal Messages for your integration product. Your product can send customized messages
to the C•CURE 9000 Journal that can be displayed on the C•CURE 9000 Activity Viewer in the Monitoring Station, and be
viewed during a C•CURE 9000 Journal Replay.

Understanding Journal Messaging


■ Journal Messaging Terminology
■ How the Journal Message System Works
■ Journal Message Types
■ Journal Message Format Data
■ Code Sample: Using the Journal Messaging Interface

Implementing Journal Messaging


1. Define a New Journal Format Base Class for your new message type.
2. Define a New Journal Message Type Class for your new message type.
3. Design and Create a New Log Format Data Class for your journal messages.
4. Add the Related Resources into the Assembly’s Resource File .
5. Defining Enumerations to Manage the String Key Words (optional) .

Journal Messaging Terminology


The following terms are used to describe the Journal Messaging system in C•CURE 9000.
Table 1: Journal Messaging Terms

Term Description

Journal Message A message type is a way to group similar messages together for easy searching and management. Under a message
Type type, you can have many different message formats to support the need for specific individual messages.

Journal Message Message Format Data stores the information about how to render a specific message. Message format data are attached
Format Data and referenced under a certain message type.

StateCode A keyword for the Monitoring Station routines that process messages. A StateCode is defined as a string in journal
message. You can add a StateCode using AddMessageItem(“StateCode”, string_value_for_action_code) from your
journaling object.

PrimaryObject Each journal message can has two optional persistent objects to be associated with. The PrimaryObject is the main
persistent object associated with a journal message.
The owner of the message needs to set the PrimaryObject property to the persistent DataServiceObject. If that cannot be
done, the PrimaryObjectKey property must be set. This includes making sure the PrimaryObjectKey.Tags property
contains a Name property, and for partitioned objects, a PartitionID.
If PrimaryObjectName is used in the format, you don't need to use AddMessageItem() to add it. The format engine will find
the name from the message's property.

SecondaryObject The additional persistent object associated with journal message. The settings for the object to the journal are similar to
those of the PrimaryObject.
NOTE: A ThirdObject and a FourthObject are now available. These Objects should be used sparingly by integrations.

MessageUTC The date/time in Coordinated Universal Time (UTC) to indicate when the message occurred. If the message comes from
a device, this is the date/time in UTC the message occurred on the device. If the message is generated from the server or
client, you can set it with MessageUTC=DateTime.UtcNow.

MessageDataTime The local date/time when the message occurred. It could be in a different time zone if the message is coming from a
different machine or device. The system calculates and stores the locale offset of the message local date/time to the UTC
date/time.

Tags A tag in the journal message is the identifier for a message item. Message items are stored in the XmlMessage property as
an XML string with the item tags as the node tags. Tags are defined in the message format data and used to set individual
message items using the AddMessageItem(). Example of the an XML message item is:
<LogMessage>
<NodeName>XU-XP</NodeName>
<WindowsCredential>AMERICAS\DXU</WindowsCredential>
<ApplicationName>MonitoringStation</ApplicationName>
<StateCode>LoggedIn</StateCode>
</LogMessage>
The NodeName, WindowsCredential, ApplicationName, and StateCode are the tags.

MessageItem A message item is the element needed to fill the parameters in the format string. Except for the PrimaryObjectName,
SecondaryObjectName, ThirdObjectName, FourthObjectName, and MessageCode, all the message items should be
added to the message using the AddMessageItem() method. Message items are stored in the XmlMessage property as a
XML string with the item tags as the node tags.

MessageCode A message code is a special message item. It is the item that determines the different kind of message under a message
type so that there should be only one MessageCode for one journal message. A MessageCode is associated with one
message format data (or the start format data for a group of message format data). MessageCode is set using
SetMessageCode(Tag, value, messageId).
Term Description

Message Partition In a partitioned system, a message's partition is based on the Primary Object's partition (as the default). However, the
owner (who created the message) can select to let this message’s partition to follow the Partition from the Secondary
Object associated with the message. The property ‘PartitionIDRule’ can be used to set the option (Unknown, Primary,
Secondary).

Message By default, a journal message will be sent to the monitor station as well as written into the journal database. However, by
Persistent Option setting the property ‘PersistOption’, you can change this to write the message to the database only without sending the
message to the monitor station (PersistOnly), or to send to monitor station but not to write the message into the database
(MonitorStationOnly), or to not send or write to both (NotPersistNotMonitorStation).
NOTE: Avoid using (NotPersistNotMonitorStation) because setting is intended only for a special case to support a driver's
internal action. Without setting this property, the default (None) setting is used, and the message is sent to both.

How the Journal Message System Works


One of the important features of the journal system is the ability to render message items stored in the journal message
into a human-readable sentence that can be translated into different languages for the cultures the system supports. The
journal format engine is designed to be independent from the messages that it formats so that new messages and new
formats can be added without the need to change the engine.

The concept of the message rendering is simple. When a message is generated, a format data should be selected within
the message type to be associated with the message instance. When the format engine receives a call for displaying the
message, the format engine reads the message items from the journal message and feed the items into the format string
stored in the format data to generate a human-readable sentence. The translation of the format string and the message
items are done before the formatting so that multiple-culture display is possible.

Journal Message Types


A message type is simply a way to group similar messages together for easy search and management. You can either
use the pre-defined message types in the system or design and add your own new message types. Whether or not you
define new message types should depend on the requirements of the your applications.

A message type is a C# class that is derived from a common base class, JournalLogTypeBase.

The base class provides the methods so that system can retrieve all the message types in the system, their names,
display names, and other information. To add a new message type, you just need to define a C# class derived from the
basic class, override some of the methods to meet any special requirements, and make sure to add the translation of the
display name for the new type into the resource in the assembly. The new message type will be picked up automatically
by the system. Please be sure to place your journal message type and format data classes into the assembly with a
‘CrossFireEnabled‘ and attribute.

The methods or properties that can be overridden from the base class include:
Method or property Description

FormatBaseType You have to override this to provide a message type specific format data base class.

ResourceName You only need to override this if you don't use the default folder and name for your resources.

Name You only need to override this if you want to manage and use short message type name.

DisplayName Suggestion: Do not override unless you perform the name translation with a different technique.

Even though you can always use the existing pre-defined message types, it is suggested that you define your own
message types for easy management. If you decide to use the existing pre-defined message types, you need to contact
Software House to obtain a range for your new format data MessageID to be added into the existing message type. This
is a necessary step to avoid possible overlapping of the message format data id from different SDK users. This step is not
needed if you define your own message type.’.

At this time, the built-in message types in the system include:

n OperatorLogin n ManualAction n NetVideoActivity


n CardAdmitted n SystemActivity n OperatorActivity
n CardRejected n SystemError n DoubleSwipe
n LogMessage n DeviceActivity
n ObjectChangedState n DeviceError

Example of a new Message Type

public class YourNewMsgType : JournalLogTypeBase


{
/// <summary>
/// Override this method to specify the format data base class
//for this message type. All the message data for this message
//type should be derived from this base type
/// </summary>
public override Type FormatBaseType
{
get
{
return typeof(YourNewMsgFormatBase);
}
}
}
Journal Message Format Data
Journal Message Format Data stores the information about how to render a specific message. Message format data are
attached and referenced under a certain message type. The format data stores the following major data:
■ A format key as a resource key for looking up the format string from the resource file;
■ A default format string that is used if the format string from resource is missing;
■ An Array of message item names and their display options that fit into the format string;
■ A MessageID that should be unique within the message type (this ID will be used when creating a journal message);
■ Other advanced format options.

Example of a Message Format Data C# Class

public class MyNewFormatData_1 : YourNewMsgFormatBase


{
/// <summary>
/// An unique MessageId in the message type of the format data
/// </summary>
public override int MessageId
{
get
{
return 1;
}
}
/// <summary>
/// The resource key for the format string in the resource
/// </summary>
public override string FormatKey
{
get
{
return "MyNewFormatDataFormatString_1";
}
}
/// <summary>
/// The default format string
/// </summary>
public override string DefaultFormatString
{
get
{
return "Device '{0}' {1}.";
}
}
/// <summary>
/// The array for the message item tags
/// </summary>
public override FormatTagData[] MessageItemTags
{
get
{
List<FormatTagData> tags = new List<FormatTagData>
{
new FormatTagData(TAG_PrimaryObjectName,
LogFormatTranslationOptions.None), new FormatTagData(TAG_StateCode,
LogFormatTranslationOptions.Resource)
};
return tags.ToArray();
}
}

LogFormatTranslationOptions

The available LogFormatTranslationOptions include: None – no translation required.

Resource – translate the item using the associated resources. uResource – same as above but convert the first character
to upper case. lResource – same as above but convert the first character to lower case. auResource – same as above but
convert all characters to upper case.
■ Date: format the item to Date using the standard Date format base on culture. Time – format the item to Time using
the standard Time format base on culture.
■ Datetime: format the item to Date/Time using the standard Date/Time format base on culture.
■ utcDatetime: convert the item (Date/Time in UTC) into local Date/Time using the standard format based on culture.
TimeSpan – format the item to TimeSpan format using the standard format base on culture.

MyNewFormatDataFormatString_1

The format string stored in the resource file with the resource key, MyNewFormatDataFormatString_1, should be "Device
'

{0}' {1}."
MessageItemTags

MessageItemTags defines two items that should fit into the format string’s two parameters. The {0} is not translatable and
the {1} is translated using the resource file.

All the resources include the format string and the message items that need translation (for example the {1} parameter
here) should be added into the resource file in the assembly where the format data class is defined.

Creating Journal Messages


To create a journal message, you need to use the journaling class:

SoftwareHouse.CrossFire.Common.Objects.Journaling4

The class implements the methods that allow you set the message items and the required information for a message
instance. Here is an example of how to use the journaling class.

try

IJournaling4 jobj = new Journaling4(typeof(YourNewMsgType));

jobj.MessageUTC = DateTime.UtcNow; jobj.MessageDateTime = jobj.MessageUTC.ToLocalTime(); jobj.PrimaryObject


= yourPrimaryObject; // if present

jobj.SecondaryObject = yourSecondaryObject; // if present jobj.ThirdObject = yourThirdObject; // if present


jobj.FourthObject = yourFourthObject; // if present jobj.SetMessageCode("StateCode", "YourStateCodeKey", 1);

ServerResult result = jobj.Send();

if (result.Exception != null) throw result.Exception;

if (result.Fault != null)

throw new Exception(result.Fault.Message);

catch (Exception ex)

//process error

The tags and message items as string keys are important for the system to identity the message items and to match the
format string and message values from the resource file. It is suggested that you use enumeration to manage your
message item keywords and the message tags to reduce the chance of typing mistakes. See Defining Enumerations to
Manage the String Key Words (optional). Be sure that the third parameter to SetMessageCode matches the desired
message format class's MessageID.
Designing and Implementing Journal Messaging
This section outlines the steps you need to follow to design your Journal Messages and integrate them into C•CURE
9000.
1. Define a New Journal Format Base Class
2. Define a New Journal Message Type Class
3. Design and Create a New Log Format Data Class
4. Add the Related Resources into the Assembly’s Resource File
5. Defining Enumerations to Manage the String Key Words (optional)

Define a New Journal Format Base Class

Define a new journal format base C# class for your new message type. The class has to be derived from:

SoftwareHouse.CrossFire.Common.Shared.MessageFormatBase

This example shows the definition of a base class for a SimplexAlarm journal format:
/// <summary>
/// The base class for the format data classes for message
/// type of SimplexAlarm
/// </summary>
public class SimplexAlarmFormatBase : MessageFormatBase
{
}

Define a New Journal Message Type Class

Define a new journal message type C# class for your new message type. The class has to be derived from:

SoftwareHouse.CrossFire.Common.Shared.JournalLogTypeBase

This example shows the definition of a journal message type class for SimplexAlarm:
/// <summary>
/// The journal message class for SimplexAlarm
/// </summary>
public class SimplexAlarmMessage : JournalLogTypeBase
{
#region IActivityMessageType
/// <summary>
/// Override this method to specify the format data
/// base class for this message type
/// </summary>
public override Type FormatBaseType
{
get
{
return typeof(SimplexAlarmFormatBase);
}
}
#endregion IActivityMessageType
}

Design and Create a New Log Format Data Class

Design and create new log format data class for your journal messages:
1. Assign a message id that is unique in this message type for the format data. This example uses 1.
2. Define your format string (a standard C# format string) for the message. Example of a format string is: “Simplex Alarm
({0}) on Panel ‘{1}’ ({2}).”
3. Add this format string into your custom project's resource with a resource key such as:
“DeviceActivityFormatString_15”;
4. Define the message item tags and display options:

Tag Display Option

AlarmType Translate using resources (Resource)

PrimaryObjectName Not translated (None)

Ack Translate using resources (Resource)

The number and order of the tags defined here should match the parameters in the format string. In the above
example, ‘AlarmType’ is mapped into the parameter {0}, ‘PrimaryObjectName’ should be mapped into the {1}, and
‘Ack’ for {2}.
5. Define the message item tags and display options:
public class SimplexAlarmFormat1 : SimplexAlarmFormatBase
{
/// <summary>
/// A unique MessageId in the message type of the
/// format data
/// </summary>
public override int MessageId
{
get
{
return 1;
}
}
/// <summary>
/// The resource key for the format string
/// in the resource
/// </summary>
public override string FormatKey
{
get
{
return "SimplexAlarmFormatString_1";
}
}
/// <summary>
/// The default format string
/// </summary>
public override string DefaultFormatString
{
get
{
return "Simplex Alarm ({0}) on Panel '{1}' ({2}).";
}
}
/// <summary>
/// The array for the message item tags
/// </summary>
public override FormatTagData[] MessageItemTags
{
get
{
List<FormatTagData> tags = new List<FormatTagData>
{
new FormatTagData("AlarmType",
LogFormatTranslationOptions.Resource), new FormatTagData("PrimaryObjectName",
LogFormatTranslationOptions.None), new FormatTagData("Ack",
LogFormatTranslationOptions.Resource)
};
return tags.ToArray();
}
}
}

Add the Related Resources into the Assembly’s Resource File

You need to add the message type name (the key is the type short name; this example uses ‘SimplexAlarmMessage’),
possible values for the AlarmType, and the possible values for the Ack into the resource file. The rule is that the message
type name resources should be added into the assembly where the SimplexAlarmMessage is defined and the others
should be added where the format data class is defined.

Defining Enumerations to Manage the String Key Words (optional)

This section is an example of the optional use of enumerations to manage your key words to avoid mistakes in typing.

Since the string tags and keys set in a journal message must match the tags in the associated LogMessageFormat object
and the string keys in the resource file, it is a good practice to define enumerations to manage the key words to avoid
typing errors. In this example, you can define a tag enum and an activity enum as:
public enum CustomTestJournalTags
{

/// <summary>
/// Tag name for activity code
/// </summary> AlarmType,

/// <summary>
/// Tag for Ack status
/// </summary> Ack
}
public enum CustomTestJournalAlarmType
{
/// <summary>
/// activity code for fire alarm
/// </summary> FireAlarm,
/// <summary>
/// activity code for fault alarm
/// </summary> FaultAlarm,

/// <summary>
/// activity code for power alarm
/// </summary> PowerAlarm,

/// <summary>
/// activity code for offline alarm
/// </summary> OfflineAlarm
}
public enum CustomTestJournalAckStatus
{
/// <summary>
/// alarm status for acked
/// </summary> Acked,
/// <summary>
/// alarm status for not acked
/// </summary> NotAcked
}

With the enumerations, the above sample can be written as:


try
{
Journaling journalObj = new Journaling(typeof(SimplexAlarmMessage));
//set messageUTC (may be the timestamp obtained from the Panel) journalObj.MessageUTC =
DateTime.UtcNow;
//set the message locale offset journalObj.MessageDateTime = DateTime.Now;
//set the primary object associated with the message
//this should be the Panel object
// jobj.PrimaryObjectKey = yourPanelObject.ObjectKey;
// jobj.PrimaryObjectName = yourPanelObject.Name; journalObj.PrimaryObjectName = "Test Panel
Name";
//use AddMessageItem to add items other than the Activity code
// you can have as many message items as you need
// parameters: tag, MessageItemValue journalObj.AddMessageItem
(CustomTestJournalTags.Ack.ToString(),
CustomTestJournalAckStatus.Acked.ToString());
//you should only have one MessageCode and only call the
// SetMessageCode once to close the message
// parameters : Tag, MessageValue, Format MessageID journalObj.SetMessageCode
(CustomTestJournalTags.AlarmType.ToString(),
CustomTestJournalAlarmType.FireAlarm.ToString(), 1);
//sent the journal message to server using Notify call ServerResult result = journalObj.Send();
if (result != null && result.Exception != null)
{
//process error
}
}
catch (MessageLoggingException)
{
//process error
}
catch (DataServiceException)
{
//process error
}

Note that if you have exactly one state code per message, you can use your state code enumeration to indicate your
message. In this case, the call to SetMessageCode would become
journalObj.SetMessageCode(CustomTestJournalTags.AlarmType.ToString(),
CustomTestJournalAlarmType.FireAlarm.ToString(), (int)CustomTestJournalAlarmType.FireAlarm);

Code Sample: Using the Journal Messaging Interface


The following code shows an example of an implementation of the Journal Messaging Interface in a product that is
integrated with C•CURE 9000.
try
{
IJournaling4 jobj = new Journaling4(typeof(SimplexAlarmMessage));
//set messageUTC (may be the timestamp obtained from the Panel instead of now) jobj.MessageUTC
= DateTime.UtcNow;
//set the message locale offset
jobj.MessageDateTime = jobj.MessageUTC.ToLocalTime();
//Set the primary object associated with the message
// this should be the Panel object (called panelObject)
// note: you don’t need to use AddMessageItem to add the message item
// for the PrimaryObject, SecondaryObject, ThirdObject, and FourthObject.
// They will be picked up automatically jobj.PrimaryObject = panelObject;

//Set the secondary Object (if used). This example shows using the ObjectKey form:
yourSecondKey.Tag["Name"] = "secondaryName"; //If not already in objKey yourSecondKey.Tag
["PartitionID"] = partitionID; //If partitioned, not already in objKey jobj.SecondObjectKey =
yourSecondKey;//yourSecondKey is objectKey of secondary object

//use AddMessageItem to add items other than the Activity code


// you can have as many message items as you need
// parameters: tag, MessageItemValue jobj.AddMessageItem("Ack", "Acked");

//you should only have one MessageCode and only call the
// SetMessageCode once to close the message
// parameters : Tag, MessageValue, Format MessageID jobj.SetMessageCode("AlarmType",
"FireAlarm", 1);

//sent the journal message to server using Notify call ServerResult result = jobj.Send();
if (result != null && result.Exception != null)
{
//process error
}
}
catch (MessageLoggingException)
{
//process error
}
catch (DataServiceException)
{
//process error
}

Using this example, A message displayed on the Monitor Station or replayed on client would be:
“Simplex Alarm (fire alarm) on Panel ‘Test Panel Name’ (Acknowledged).”
Where ‘Test Panel Name’ is the Panel object name and the ‘fire alarm’ is the translated AlarmType and the
‘Acknowledged’ is the translated StatusACK.

See the following screen capture examples of the sample alarm journal message shown on Monitor Station and the same
message re-played from the Journal Replay Dynamic View:
Figure 27: Sample Alarm Journal Replay

Figure 28: Sample Alarm Journal Message

Visual Studio Templates


The templates are designed to simplify the task of generating code to interface with Crossfire and hence the C•CURE
9000 system. The templates need to be installed into the development environment in order to function and do not add any
assemblies or code other than that which is created via their execution to the resultant system.

Installing the Templates

There are code generation templates for Visual Studio. These templates generate code which assists in writing plug-ins
for the CrossFire system. There is a template that will write the outline code and project for a client component.

Using the Templates

To use this template, typically you add the template to an existing project that already has one or more classes defined
(but you can add it to a new project and build the classes subsequently).
You can customize the template, for example, to modify the namespace by editing the source files in the zip file. You
unzip the template, edit the template text file, then zip it again to use it with Visual Studio. Refer to the documentation on
MSDN for more information on this technology in Visual Studio.

To add the Template to a new or existing project:


1. In Visual Studio, open an existing project or create a new project.
2. Create a new folder in the project to which you wish to add a client component. Name the folder after the class name
that you want this client component to represent.
3. Right click the new folder and select Add New Item.
4. Select the CrossFire Client component then change the source file name of the client component to the name you
wish the class to be.
The code will be generated and inserted into this folder and may be edited normally from there.

Maintenance-Mode
Maintenance Mode is used to limit information about an object displayed on the Monitoring Station. Maintenance Mode
only affects what is reported at the Monitoring Station. For any object that participates in Maintenance Mode, the mode
can be turned on or off.
■ If Maintenance Mode is turned on, the activity associated with the object will not be shown at the Monitoring Station.
■ If Maintenance Mode is turned off, the activity associated with the object will be shown at the Monitoring Station.
Maintenance Mode is typically used during installation of hardware into the system, or hardware being serviced or
tested.

9000 Objects that Support Maintenance Mode


The following objects are supported in Maintenance Mode:
■ apC Comm Ports
■ apC Controllers
■ apC Add-On Boards
■ apC I32 Input Boards
■ apC I8 Input Boards
■ apC R48 Output Boards
■ apC R8 Output Boards
■ apC Inputs
■ apC Readers
■ apC Doors
■ Areas
■ C•CURE Mobile
■ Elevators
■ Events
■ Floors
■ Intrusion Zones
■ Keypad Commands
■ iSTAR Clusters
■ iSTAR Controllers
■ iSTAR Doors
■ iSTAR Inputs
■ iSTAR Readers
■ iSTAR Aperio Hub Boards
■ iSTAR Aperio Readers
■ iSTAR Aperio Doors
■ iSTAR PIM-485 Readers
■ iSTAR Device Ports
■ iSTAR ACM Boards
■ iSTAR Input Boards
■ iSTAR Output Boards
■ iSTAR Ultra ACMs
■ Outputs
■ Star Coupler Ministar
■ Star Coupler Star
■ Star Coupler WPSC

Privileges for Maintenance Mode


There are two privileges associated with Maintenance Mode:
■ Turn Maintenance Mode On
■ Turn Maintenance Mode Off

Only Operators who have the Turn Maintenance Mode On and Turn Maintenance Mode Off Privilege assigned to them can
put an object into Maintenance Mode and take an object out of Maintenance Mode.

An Operator who has the System All Privilege will have Turn Maintenance Mode On and Turn Maintenance Mode Off
Privilege by default.

Each partner object that participates in Maintenance Mode should add the following executors in the RegisterExecutors
method override in their client component file:
Executors.Add(typeof(MaintenanceModeOnExecutor).FullName, new MaintenanceModeOnExecutor());
Executors.Add(typeof(MaintenanceModeOffExecutor).FullName, new MaintenanceModeOffExecutor());
The RegisterExecutors method for the Floors client component is shown below as a sample:
/// <summary>
/// Override to register the Floor executor.
/// </summary>
/// <remarks>
/// For the Floor we are going to use the fully qualified type of the class
/// for the key.
/// </remarks>
public override void RegisterExecutors()
{
Utilities.XFTrace(_traceSwitch, TraceLevel.Verbose, "In FloorClientComponent
RegisterExecutors");
// Call the base class to get the default executors.
base.RegisterExecutors();

// Remove the ones we don't want


Executors.Remove(DefaultVerbs.PopupView);
Executors.Remove(DefaultVerbs.ViewCurrentTab);
Executors.Remove(DefaultVerbs.View);

Executors.Add(typeof(MaintenanceModeOnExecutor).FullName, new
MaintenanceModeOnExecutor());
Executors.Add(typeof(MaintenanceModeOffExecutor).FullName, new
MaintenanceModeOffExecutor());
}

Registering the executors results in allowing users the ability to grant the Privilege to Turn Maintenance Mode On and

Turn Maintenance Mode Off as needed. The Floor privilege is shown as an example.
Figure 29:

Maintenance Mode Field


The Maintenance Mode value is stored in MaintenanceMode field. It is defined as (bit, not null).

If the partner object derives from one of the 9000 objects listed in 9000 Objects that Support Maintenance Mode , the field
is defined in the table as well as the partner object and no additional code is needed for the partner object.

If the partner object does not derive from one of the 9000 objects listed as supporting Maintenance Mode, a field named
MaintenanceMode defined as (bit, not null) needs to be added to the partner table.

The definition of the partner object needs to include

SoftwareHouse.CrossFire.Common.ObjectDefinitions.IMaintenanceMode. The Floors definition is shown below as a


reference:
public interface IFloor : SoftwareHouse.CrossFire.Common.ObjectDefinitions.IObject,
SoftwareHouse.CrossFire.Common.ObjectDefinitions.IImportableEntity,
SoftwareHouse.CrossFire.Common.ObjectDefinitions.IMaintenanceMode
{
The following code should be added to the persistence file for the partner object (replace references to "floor" with the
partner object type):
/// <summary>
/// MaintenanceMode.
/// </summary>
/// <value>MaintenanceMode(Boolean).</value>
[PersistentProperty("MaintenanceMode", DbType.Boolean, Nullable = true, IsIndex = false,
DefaultValue = false)]
[ToolTipText(typeof(Floor), "FloorMaintenanceModeToolTipText")] [UserSelectable
(UserSelectableParams.ReadOnly)]
[UserInterfaceProperty(ControlType =
"SoftwareHouse.CrossFire.Client.ClientComponentsFramework.ControlLibrary.CheckBox", QuickSearch
= false)]
public virtual bool MaintenanceMode
{
get
{
object val = this["MaintenanceMode"]; if ((null == val))
{
return false;
}
else
{
return ((bool)(val));
}
}
set
{
this["MaintenanceMode"] = ((bool)(value));
}
}

Maintenance Mode Control in the Partner Editor


The 9000 Form Template has the Maintenance Mode Check Box control defined. The control is disabled by default and
not visible.

To make the control visible on the partner object editor, place the following code in the load of the editor (replace
references to "floor" with the partner object type)
IClientComponent component = ClientComponentsManager.GetComponent(_floor.ClassType);
string componentFullName = component.GetType().ToString();
// Check if the current operator has rights to turn maintenance mode on and off
if (ClientComponentsManager.TheCurrentOperator.CanDo(component.GetType().ToString(),
MaintenanceModeOnExecutor.MenuID, _floor.ObjectKey) &&
ClientComponentsManager.TheCurrentOperator.CanDo(component.GetType().ToString(),
MaintenanceModeOffExecutor.MenuID, _floor.ObjectKey))
{

maintenanceModeCheckBox = ParentForm.Controls["checkBoxMaintenanceMode"] as
System.Windows.Forms.CheckBox;
if (maintenanceModeCheckBox != null)
{
// Need to enable and make visible as form template setDefaults controls have not been done yet
MaintenanceModeUtilities.EnableCheckBox(maintenanceModeCheckBox);
// Set unbound tooltip
this.FloorToolTip.SetToolTip(maintenanceModeCheckBox,
Properties.Resources.FloorMaintenanceModeToolTipText);
maintenanceModeCheckBox.Checked = _floor.MaintenanceMode;
}
}
else
{
// disable the Maintenance Mode check box
maintenanceModeCheckBox = ParentForm.Controls["checkBoxMaintenanceMode"] as
System.Windows.Forms.CheckBox;
MaintenanceModeUtilities.DisableCheckBox(maintenanceModeCheckBox);
}
private Floor _floor = null;

MaintenanceModeUtilities is in the SoftwareHouse.CrossFire.Client.Core namespace; add a using for this


namespace.

MaintenanceModeOnExecutor is in the SoftwareHouse.CrossFire.Client.ClientComponentsFramework


namespace; add a using for this namespace.

The Maintenance Mode Check Box should then show up on the partner object editor after rebuilding the code.

Monitor
Monitor is used in the Administration Station to display the current state and monitor activity for only selected objects. An
object that participates in Monitor can display the state icon and activity for itself and its child objects. The child objects
can be objects in the existing child collection as well as specified with an attribute.

Monitor is accessed as a Context menu selection for an object.


Figure 30: Monitor Example

Registering Monitor in the Context Menu


Monitor is activated by clicking on an object in a Dynamic View or Hardware/Video tree and selecting Monitor from the
context menu.
Figure 31:
To enable an object to be monitored for activity on its own, add the following executor in the RegisterExecutors method
override in the client component file:
Executors.Add(typeof(AdminMonitorExecutor).FullName, new AdminMonitorExecutor ());

The RegisterExecutors method for the client component is shown below as a sample:
public override void RegisterExecutors()
{
// Call the base class to get the default executors.
base.RegisterExecutors();
Executors.Add(typeof(AdminMonitorExecutor).FullName, new AdminMonitorExecutor
());
}

Having the executor registered for an object results in the addition of the Monitor permission to the Privilege for the object.

Privilege for Monitor


There is a privilege associated with Monitor.

Only operators that have the Monitor permission selected in their assigned Privileges can bring up the Monitor Form. The
following image shows an example of the Monitor permission for the iSTAR Input object.
Figure 32: Monitor Permission In Privileges

Registering Child Objects to Monitor


Objects automatically display themselves and what is in their child collection as a child object to Monitor. If another object
should be shown as a child of an object, an attribute should be added to the child object type.

Example:

A Reader is used in a Door, but the Reader does not show up in Monitor since it is not in the Door child collection. Add the
MonitorLookUpId attribute to the Reader object which describes how to be a child of the Door.

The MonitorLookUpId attribute attempts to describe the following:

When the Associated Object has direct knowledge of the current object, use a simple mapping to describe the
relationship.
[MonitorLookUpId(typeof(iStarDoor), "InReaderID", "ObjectID")]
public partial class iStarReader

The Parameters from left to right are:


■ ParentObjectType – This is the Parent object type. This is the “Door” in the reader / door example.
■ ParentObjectIdProperty – The Parent object contains a property that points to the current object. Enter that property
name here. The parent Door object has an “InReaderID” property that points to the Reader object Id.
■ ChildObjectIdProperty – The current/child Object has a unique identifier column that maps to the
ParentObjectIdProperty. The example would have a column in the Reader Object called “ObjectID”.

Show Associations
Show Associations is used to display objects that are associated with a selected object but are not a child of the selected
object. An object that participates in Show Associations displays a count of object types it has a relationship with, and
then the user may get a dynamic view of the associated types.

Show Associations is typically used during an investigation that needs to discover what other objects use the selected
object.

Show Associations is accessed as a Context menu selection for an object.


Figure 33: Show Associations Example

Registering Show Associations in the Context Menu


Show Associations is activated by clicking on an object in a Dynamic View or Hardware/Video tree and selecting Show
Associations from the context menu.
Figure 34: Show Associations in the Context Menu

To enable an object to discover and show its associations, add the following executor in the RegisterExecutors method
override in their client component file:
Executors.Add(typeof(MaintenanceModeOnExecutor).FullName, new MaintenanceModeOnExecutor ());

The RegisterExecutors method for the client component is shown below as a sample:
public override void RegisterExecutors()
{
// Call the base class to get the default executors. base.RegisterExecutors();
Executors.Add(typeof(ShowAssociationExecutor).FullName, new ShowAssociationExecutor
());
}

Having the executor registered for an object results in the addition of the Show Associations permission to the Privilege
for the object.

Privilege for Show Association


There is a Privilege associated with Show Association.

Only operators that have the Show Associations permission selected in their assigned Privileges can view an object's
associations. The following image shows an example of the Show Associations permission for the iSTAR Input object.
Figure 35: Show Associations Permission in Privileges

Registering Associated Objects


Objects do not inherently know how to discover the other objects with which they are associated. Each object must be
told what other objects may be associate with it.

Example:

An Input is used in a Door as a Door Switch Monitor. The Input does not know that it has a relationship to the door. To
allow the Input to have knowledge of a relationship to the Door, add the ShowAssociation attribute to the Input object
which describes how to discover the relationship.

The ShowAssociation attribute attempts to describe:


■ One to One relationships
■ One to Many relationships

Simple 1 to 1 Relationships.

When the Associated Object has direct knowledge of the current object, use a simple mapping to describe the
relationship.
[ShowAssociation(typeof(iStarDoor), " DoorSwitchID", "ObjectID")]

The Parameters from left to right are:


■ AssociatedObjectType: This is the associated object type that will be displayed. This is the “Door” in the input / door
example.
■ AssociatedObjectIdProperty: The associated object may contain a property that points to the current object. Enter
that name here. The associated Door object has a “DoorSwitchID” that points to the Input object Id in a simple 1:1
relationship.
■ CurrentObjectIdProperty: The current Object has a unique identifier column that maps to the
AssociatedObjectIdProperty. The Input example would have a column in the Input Object called “ObjectId”.

One to Many Relationships

When a 1 to many relationship is used, there is an intermediate table used to store one to many relationships. The
intermediate table is used in the attribute description. An example is that an iStar Door does not know it is a member of an
Intrusion Zone.
[ShowAssociation(typeof(iStarIntrusionZone), "ObjectID", "ObjectID", typeof
(DoorIntrusionZoneLink), " IntrusionZoneID","DoorId")]

The Parameters from left to right are:


■ AssociatedObjectType: This is the associated object type that will be displayed. This is the “Intrusion Zone” in the
door / intrusion zone example.
■ AssociatedObjectIdProperty: The associated object contains a property that points to it’s own unique identifier.
This would mean that the iStarIntrusionZone has a unique id of “ObjectID” and that value is used in the link table.
■ CurrentObjectIdProperty: The current object contains a property that points to it’s own unique identifier. This would
mean that the iStarDoor has a unique id of “ObjectID” and that value is used in the link table.
■ LinkTableType: This is the name of the Object Type that is the link table.
■ AssociatedPropertyForLinkTable: This is the property name in the Link Table Object to identify the uniqueidentifier
for the associated Object.
■ CurrentPropertyForLinkTable: This is the property name in the Link Table Object to identify the uniqueidentifier for
the current decorated Object.

Putting it all Together


Once all your client components and navigation classes are coded, they can be deployed and used inside the C•CURE
9000 application or inside a custom application. If the use case is to extend the client applications, the following sections
should be skipped. If not, the following sections explain how to create a separate application that uses the framework.

Implementers can create a windows forms application using Visual Studio 2010 for use with the framework. After creating
the application they should edit the program.cs file to include the following steps.

Object Type Loading


All the data service object types must be loaded into the space of the application. There is a class called TypeManager
inside the Common.Shared assembly that facilitates this operation.
TypeManager.Instance.LoadAllAssemblies(AppDomain.CurrentDomain.
BaseDirectory, true);

Connecting to the Server


The new client application needs to have a connection to the application server to be able to interact with it.
1. Use the ClientConnectionManager object in the Common.ClientInterfaceLayer assembly to establish a connection
the following way:
ClientServerConnection.Instance.ConnectToServer();
2. Modify the app.config file of the application to configure the Microsoft Windows Communication Framework. The
following is a sample of the minimal configuration of such file:
<system.serviceModel>
<bindings>
<netTcpBinding>
binding name="ServiceBinding" maxReceivedMessageSize="2147483647"
receiveTimeout="24.0:00:00" sendTimeout="0.0:20:00">
<readerQuotas maxArrayLength="2147483647" maxBytesPerRead="2147483647"
maxDepth="2147483647" maxNameTableCharCount="2147483647"
maxStringContentLength="2147483647" />
<reliableSession enabled="false"
ordered="true"/>
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint name="ClientSession"
address="net.tcp://localhost:8999/CrossFire/IClientSession"
binding="netTcpBinding" bindingConfiguration="ServiceBinding"
contract="SoftwareHouse.CrossFire.Common.
ClientInterfaceLayer.IClientSession">
</endpoint>
</client>
</system.serviceModel>

Loading all the Client Components


All the client components shall be also loaded into the new applications space. This is done with the help of the
ClientComponentsManager object in the Client.ClientComponentsFramework assembly. This is done the following way:
ClientComponentsManager.LoadClientComponents();

Using the Configuration Control


After coding the above three steps, the new program is hooked into both the client and the server framework. There is no
need to do anything else as far as setup is concerned. Now the question is what parts of the framework is the implementer
going to use. Inside the Client.NavigationFramework there is a control which has been mentioned before called the
ConfigurationControl. This control is what makes the C•CURE 9000’s outlook style. Implementers can reuse this look if
applicable by incorporating this control into their main windows form.
Personnel Framework
This chapter describes C•CURE 9000 Personnel Framework and explains how to build Personnel Components for a
Connected Program integrated product.
■ Personnel Framework
■ Custom Field Types for Personnel User-defined Fields
■ Using the Personnel Framework in Your Integration

Personnel Framework
The personnel framework allows seamless integration between C•CURE 9000 Personnel entries and external customer-
visitor-management APIs.

Example:

You can use the framework to pair C•CURE 9000 Personnel records with biometric credentials such as fingerprint swipes
or retinal scans, even though C•CURE 9000 does not natively support this functionality.

The components of this Framework are:


■ Client Component and Control.
■ Data.
■ Custom Personnel Control Sample.

Client Component and Control

The Client Component, the primary element of the personnel framework, hosts a control with custom logic that can
communicate with systems external to C•CURE 9000. Using this custom control—which is integrated directly with the
C•CURE 9000 Personnel View through the Client Component described above, you can add logic to pair Personnel
entries in C•CURE 9000 with data from external systems with the help of a WPF or Windows Forms GUI.

Data
The Personnel Framework also allows you to store enrollment data from another system using a serialized class.

A class used for this purpose may be very simple, containing only getters/setters (or properties) designed to hold any data
from external systems. The benefit of this mechanism is that responses can be stored and consequently checked in the
future—without having to re-query the originating API.

When the control portion of the framework requests the data class, the class must be de-serialized before being used, and
conversely, serialized once again before being stored. These classes have a one-to-one relationship with C•CURE 9000
personnel entries—each serialized class can only contain data specific to one individual.

To synchronize reliably across Enterprise Systems. these serialized classes can contain no more than a total of five (5) MB of data.
If more than five (5) MB are stored in this mechanism, synchronization between the MAS and SAS systems may behave
unpredictably.
You can store the external data used by the personnel framework control in other ways:
■ By creating a new table in ACVSCore – which allows you to still use CrossFire to access data from this table, but will
not synchronize the data over a C•CURE 9000 Enterprise System
■ By connecting to a different database instance altogether – which requires that you include your own queries to
access and use the data.
■ When you do not need to save external system data in C•CURE 9000 for easy retrieval later on, you can omit the
data portion of the personnel framework. You can use the control portion by itself.

Custom Personnel Control Sample

The Custom Personnel Control Sample included in the Connected Program Kit exemplifies the Control and Data portions
of the Personnel Framework. See PersonnelExtSamples.

Custom Field Types for Personnel User-defined Fields


C•CURE 9000 Custom Personnel User-defined Field types allow partners to extend the C•CURE 9000 Personnel Editor
via the use of User-defined Fields. Partners have the option to write Custom Control Objects, Client Components, and
User-defined Controls that display in the User-defined Fields Editor as a Custom Field Type. Users can then drag and
drop these Custom Controls anywhere they wish—as they do the current User-Defined Fields.

Partners can write custom functionality from within the Personnel Editor, and in addition, persist extended properties to an
extended table linked to the current Personnel object.

How Personnel Field Types Appear in the User-defined Fields Editor


The Custom Field Type has been added to the User Defined Fields Editor. When you select this Custom Field Type, a
Custom Control Combo Box appears displaying all Client Components of the following base type, as shown in the
following image:

SoftwareHouse.CrossFire.Client.CustomControlClientFramework.CustomControlBaseClientComponent.
Figure 36: User-defined Fields Editor with Custom Control

Selecting a component of the preceding base type automatically populates the User-defined Fields tab of the Personnel
Views editor with this custom control. The custom control then appears as the type of control assigned to this particular

User-defined Field and can be placed anywhere on the Personnel Views designer similarly to the existing User-defined
Field Types, as shown in the following image:
Figure 37: Personnel Views Layout Designer Tab with WPF Button Custom Control

When a custom User-defined Field is created, a new database column of type varbinary(MAX) is created in the [Access].
[PersonnelUDF] database table. This column represents the serialized data belonging to the Custom User-defined Field’s
control data, as defined in C•CURE Personnel Framework Base Control Data.

C•CURE Personnel Framework Base Control Data


The C•CURE Personnel Framework Base Control Data class must be built in a separate data or common assembly and
installed in the Tyco\Crossfire and Tyco\CCure Client directory. You can provide Custom Control information for your
integration derived from this base class.

This class also contains the properties for all custom data belonging to the custom User-defined Field’s control. The
properties in this class are serialized and de-serialized using compression and are stored in the PersonnelUDF’s or
CredentialUDF’s varbinary(MAX) field by the Personnel SDK framework. To pass UI and Data Service Layer validation,
all serialized data is limited in size to 4000 KB (~4MB). This limitation is necessary to allow Enterprise Synchronization
services to run successfully.

The derived SoftwareHouse.CrossFire.Common.CustomControlDataFramework.CustomControlBaseData class


contains the following key Attributes and Properties:
■ Serializable: Class Attribute required to specify to the Personnel Framework that this class is intended to be
serialized.
■ NonSerialized: Property Attribute required to specify to the Personnel Framework that this property is not intended to
be serialized.
■ Validate: Class Method called by Personnel Framework before serialization and de-serialization of the custom
control data is performed. You must Validate your data assigned to that instance of the class. (Ex this.String1 ==
“foo”).

C•CURE Personnel Framework Base Control Client Component


The C•CURE Personnel Framework Base Control Client Component Class must be built in a separate client assembly
and installed in the Tyco/CCure Client directory. You can provide Custom Control information for your integration derived
from this base class.

The derived SoftwareHouse.CrossFire.Client.CustomControlClientFramework.CustomControlBaseClientCom ponent


Client Component class contains the following key Attributes and Properties:
■ SoftwareHouse.CrossFire.Client.Core.ClientComponentAttribute: Class Attribute that specifies the name of the
Client Component linked to this Custom Control.
■ ClientComponentEditUI: Property Attribute that specifies the
SoftwareHouse.CrossFire.Client.CustomControlClientFramework. CustomControlBase type. This is the class that
derives from SoftwareHouse. CrossFire.Client.CustomControlClientFramework.CustomControlBase and is the
control assigned to the custom User-defined Field defined in C•CURE Personnel Framework Base Control.

C•CURE Personnel Framework Base Control


The C•CURE Personnel Framework Base Control Class must be built in a separate client assembly and installed in the
Tyco\CCure Client directory. You can provide Custom Control and Data information for your integration derived from this
base class.

The derived SoftwareHouse.CrossFire.Client.CustomControlClientFramework.CustomControlBase

Custom Control class contains the following key Attributes and Properties:
■ Custom Control: Class Attribute that specifies the Control's
SoftwareHouse.CrossFire.Common.CustomControlDataFramework.CustomControlData class, defined in C•CURE
Personnel Framework Base Control Data
■ IsChanged: Set to true if data on the Custom Control has changed.
■ Cancel: Call to cancel any editing that has been performed.
■ Initialized: Call once the Custom Control has been initialized.
■ Reset: Call to re-set the control back to a default state.
■ Saving: Call before the Form Template saves the Custom Control. ( With this method, all UI specific validation must
be performed. Based on the result of the UI validation, you must return a Boolean value.)
■ CustomControlType: The Custom Control Type: WinForms or WPF.
■ WPFControl: If the Custom Control Type is WPF, specify the type of WPF Control to be loaded.
■ DataObject: The instance of the parent Personnel or Credential Object.
■ CustomControlData: The instance of the derived
SoftwareHouse.CrossFire.Common.CustomControlDataFramework.CustomControlBaseData class defined in
C•CURE Personnel Framework Base Control Data

Using the Personnel Framework in Your Integration


The Personnel Framework gives you the following capabilities:
■ To design a Custom Control for the Personnel editor. (The Custom Control you create may/may not have extended
data associated with it.)
■ To implement custom functionality within the Custom Control.

Example:

You may want to read card data from a Reader and automatically create the associated Credential record. (The Custom
Control you create may/may not have extended data associated with it.)
■ To link extended data fields to a specific Personnel object.

Example:

You may want to link an electronic badge with a Hexadecimal ID to a specific Personnel Object.

Reports

If you develop a Custom Control integration and store all data in User-defined Fields, a user can generate Reports for all
the Custom Controls in C•CURE 9000. The Report for the Personnel Object will show the User-defined Field, but the data
will not be visible.

Dynamic Views
If you develop a Custom Control integration and store all data in User-defined Fields, a user can generate Dynamic Views
for all the Custom Controls in C•CURE 9000. The Dynamic View for the Personnel Object will display the User-defined
Field, but the data will not display.

Queries
If partner has developed a Custom Control integration and is storing all data in User-defined Fields, a user can generate
Queries for all the Custom Controls in C•CURE 9000. The Query for the Personnel Object will display the User-defined
Field, but the data will not display.
Import/Export
If you develop a Custom Control integration and store all data in User-defined Fields, a user can import/export all the
Custom Controls in C•CURE 9000. The Import/Export for the Personnel Object will include the serialized control data.

C•CURE Enterprise Support


If you develop a Custom Control integration and store all data in User-defined Fields, this data can be viewed on all SAS
computers, as well as on the MAS computer remote Clients where the integration is installed. If you have chosen to store
Custom Control data in a local object, this data can be viewed only on that local SAS computer. Migrations from a
Standalone C•CURE 9000 to a SAS are not supported if local objects are being used.

C•CURE Personnel SDK Privileges


A user shall be able to control all Privileges on all the Custom Control objects in C•CURE 9000. If you have chosen to
store Custom Control data in a serialized User-defined Field, the Privileges will be applied to the Personnel object.

Journal and Audit


A user shall be able to view Journal and Audit reports on all the Custom Control objects in C•CURE 9000. If you have
chosen to store Custom Control data in a serialized User-defined Field, the view Journal and Audit reports will be applied
to the Personnel object.

Backup and Restore


A user shall be able to view backup and restore all the Custom Control objects in C•CURE 9000. If you have chosen to
store Custom Control data in a serialized User-defined Field, the Custom Control data will be contained with the
Personnel object and/or User-defined Field.
Video Framework
This chapter describes C•CURE 9000 Video Framework and explains how to build Video Components for a Connected
Program integrated product.
■ Video Framework
■ Adding Your Component to the Video Tree
■ Client Support for Video
■ Server Support for Video
■ Third-party integration into SOC View
■ Third-party integration into Video Search
■ Third-party integration with Clip Management
■ Video Feature Support

Video Framework
To integrate video into the C•CURE 9000 system you must provide the following components:
■ Video Server DataService Object
■ Video Camera DataService Object
■ Video Server Client Component
■ Video Camera Client Component
■ Video Server Component
■ Video CrossFireTreeExtender Client Component

See Adding Your Component to the Video Tree for information about making your integration visible in the Video pane.

Video Integration Project Templates


Sample projects that can be used as the basis for Video Integration are provided as templates in Visual Studio when you
install the Connected Program Kit. You can open Visual Studio and click on Visual C#>Windows to access the
templates.

There are three templates in the My Templates section.


■ SampleVideoClient
■ SampleVideoObjects
■ SampleVideoServerComponent

Create a solution and add all three projects to the solution. Once you have done this, you will have all the basic
architecture of the implementation for a video integration, with all the user controls and files added.
Make sure to set the Application Target Framework for each Template Project to .NET Framework 4.8. In
NOTE Visual Studio, right click on each template project in the Solution Explorer and select Properties. In the
Application tab, select .NET Framework 4.8 from the Target Framework list.

For convenience, a Sample Video Integration built using the foregoing Video Project Templates and
updated to target .NET Framework 4.8 is available in the CCURE_CPK_Partner_ISO folder that you
download from the Connected Partner Portal.

These files provide a wrapper to which you can add your code to perform video integration functions. The files include
placeholders comment with "TODO" to indicate where you need to call the API for your integration to perform the
respective functionality needed.

Software House highly recommends that you use these templates for creating a video integration with the Connected
Program Kit. However, the Video Framework documentation in this topic and referenced above also include the detailed
steps of the workflow for manually creating all these projects. You can refer to the Video Framework sections of the
reference documentation (CCURE-9000-SDK-Programmers-Guide.chm) for information about using specific function
calls.

Data Service Objects


Both the video server and the video camera have to be derived from the already existing video classes. There is no need
to create database tables for these objects since the Access.VideoServer and Access.VideoCamera tables are already
there for you to use. What you do need to do is create a new assembly to hold the implementations of the two needed
classes. The following is what the classes shall look like:
namespace VideoVendorVideoObjects
{
[DataContract(Namespace = "VideoVendorVideoObjects")]

public class VideoVendorVideoServer : VideoServer


{}
[DataContract(Namespace = "VideoVendorVideoObjects")]

public class VideoVendorVideoCamera : VideoCamera


{}
}

The following are the dependencies needed for the project:


■ SoftwareHouse.CrossFire.Common.Core.dll
■ SoftwareHouse.CrossFire.Common.DataServiceLayer.dll
■ SoftwareHouse.CrossFire.Common.ObjectDefinitions.dll
■ SoftwareHouse.CrossFire.Common.VideoObjectDefinitions.dll
■ SoftwareHouse.CrossFire.Common.VideoObjects.dll
The build location for this new assembly shall be in the following directories:
■ Tyco\CrossFire
■ Tyco\CrossFire\CCURE Client
■ Tyco\CrossFire\ServerComponents

CrossFireEnabled

The video client, video server, and video objects should be CrossFireEnabled().
[assembly: CrossFireEnabled()]

The video objects assembly should contain the information about the DbConnection.
[assembly: DbConnection("DATA SOURCE=.;INITIAL CATALOG=.;INTEGRATED SECURITY=TRUE",
ProviderName="System.Data.SqlClient")]

Both these attributes are defined in the SoftwareHouse.CrossFire.Common.Core.dll.

Validation
Since different video servers may support different numbers of cameras, a protected member called totalCameras is used
to set these limits. Note that the number entered in this field will also be the one the user interfaces use to validate as well.
This field shall be setup in the Initialize override of the derived video camera Data service object in the following way:
protected override void Initialize(bool loadContainer)
{
// The total number of cameras supported by a server. this.totalCameras = 150; base.Initialize
(loadContainer);
}

Adding Your Components to the Video Tree


If you want your integration component to appear in the Video Tree and Video Pane drop-down list, you need to do the
following:

Step See...

1. Derive the integration's CameraClientComponent from VideoCameraClientComponentBase. Client Support for Video
Step See...

2. Derive the integration's ServerClientComponent from VideoServerClientComponentBase. Server Support for


Video

3. Derive your Custom VideoTreeExtender Client Component from the


CrossFireTreeExtender

4. License your integration with Software House.

CrossFireTreeExtender Class
The key to the integration is done by deriving a class from
SoftwareHouse.CrossFire.Client.NavigationFramework.CrossFireTreeExtender and overriding the required methods
depending on your requirements. The Video tree at startup looks up all the derived classes and uses those objects from
them to build on the tree as well as to describe their behavior.

References

The required references are the following:


■ SoftwareHouse.CrossFire.Client.NavigationFramework.dll
■ SoftwareHouse.CrossFire.Client.ClientComponentsFramework.dll
■ SoftwareHouse.CrossFire.Common.Interfaces.dll
■ SoftwareHouse.CrossFire.Client.Core.dll

Overridable Methods

The following is the list of virtual methods that implementers can override.

SupportedTypesForNode

public override Type[] SupportedTypesForNode(Type node, ObjectKey key)

This method controls what type of objects are loaded by the Video tree at each level. This method is called by the tree
control when it needs the types of objects that it needs to load at each level. Implementers shall return an array of types
that are supported at each particular level of the tree. Two things to keep in mind, the first is that a value of null for the
arguments means that the tree is loading its root. The second, being that you should always include a case for a node of
type VideoFolder The following is an example of what an implementation might look like:
{
if (node == null)
{
return new Type[] { typeof
(SoftwareHouse.CrossFire.Common.VideoObjects.VideoFolder) };
}
if (node == typeof(SoftwareHouse.CrossFire.Common.VideoObjects.VideoFolder))
{
return new Type[] { typeof(VideoVendorVideoServer) };
}
else if (node == typeof(VideoVendorVideoServer))
{
return new Type[] { typeof(VideoVendorCamera) };
}
return null;
}
public override Type[] SupportedTypesForNode(Type node)
{
if (node == null)
{
return new Type[] { typeof
(SoftwareHouse.CrossFire.Common.VideoObjects.VideoFolder) };
}
if (node == typeof(SoftwareHouse.CrossFire.Common.VideoObjects.VideoFolder))
{
return new Type[] { typeof(VideoVendorVideoServer) };
}
else if (node == typeof(VideoVendorVideoServer))
{
return new Type[] { typeof(VideoVendorCamera) };
}
return null;
}

SupportedTypesForCreation

public override Type[] SupportedTypesForCreation (Type node){


return this.SupportedTypesForNode(node);
}
ClassFoldersImageList

public override ImageList ClassFoldersImageList


{
get
{
ImageList imageList = new ImageList();
imageList.ColorDepth = ColorDepth.Depth32Bit;
return imageList;
}
}

DisplayTextForClassFolder

Override this method to customize the name of your VideoServer and VideoCamera objects which will be displayed in the
Video Dropdown Menu. You can define the string values in the Resources.resx file.
public override string DisplayTextForClassFolder(Type node)
{
if (node == typeof(VideoVendorVideoServer)){
return Properties.Resources.VendorVideoServer;
}
if (node == typeof(VideoVendorCamera)){
return Properties.Resources.VendorVideoCamera;
}
return string.Empty;
}

SupportedTreeType

Configure your SupportedTreeType as Video so that your custom VideoCamera and VideoServer will populate in the
Video Tree, not the Hardware Tree.
public override HardwareTreeType SupportedTreeType
{
get
{
return HardwareTreeType.Video;
}
}
Client Support for Video
A new assembly shall be created to hold all the client support for the video integration. This assembly shall contain the
following classes which will all derive from classes supplied by the SDK:
■ VideoVendorVideoServerClientComponent
■ VideoVendorVideoServerEditor
■ VideoVendorVideoCameraClientComponent
■ VideoVendorVideoCameraEditor
■ VideoVendorVideoCameraViewer
■ VideoTreeExtender

Video Server Client Component


The video server client component will encapsulate the functionality of the video server client support. A derived class
from VideoServerClientComponentBase must be generated to encapsulate the video vendor’s functionality.

Overrides

There is only one override that is important and that is the CameraType property. This property shall return the type of
video camera the video server supports. The framework uses this type to do object associations in some parts of the
system. An example of this is the video tree. The video tree uses this association to build the hierarchy and ownership of
the video objects.

The following is a sample of what that derived call shall look like: [ClientComponent(typeof(VideoVendorVideoServer),
DefaultVerb=DefaultVerbs.Edit)]
public class VideoVendorServerClientComponent :
VideoServerClientComponentBase
{
#region Constructor
/// <summary>
/// Default Constructor
/// </summary>
public VideoServerClientComponent()
{}
#endregion

#region Create Controls


/// <summary>
/// Edit control to display for the editor.
/// </summary>
/// <value><see cref="VideoVendorComponentEditControl"/> object
/// defining the editor.</value>

[ClientComponentEditUI]
public Control ClientComponentEditUI
{
get { return new VideoVendorServerEditor(); }
}
#endregion

#region public overrides


/// <summary>
/// The video server component needs to know what type of
/// cameras it supports. This is used in the client application
/// to figure out associations.
/// </summary>
public override Type CameraType
{
get
{
return typeof(VideoVendorCamera);
}
}
#endregion
}

Video Server Editor

To inherit the user interface of the video server you must create an inherit user control using Developer Studio. When you
add the new item to the project you must select the “inherited user control” item shown by the following window:
Figure 38:

After selecting the type of item, a window allowing you to browse to the
SoftwareHouse.CrossFire.Client.VideoClientComponents.dll will be displayed. Search and find the video server base to
inherit from.
Figure 39:

Once the class has been created the following user interface is inherited:
Figure 40:

Overrides

The fields indicated by red ellipses on the preceding screen are the most important aspects that an integrator must take
into consideration when integrating a video feature set.

Video Protocol Label

The video protocol label is there to show the user which video protocol manufacture is the video server being setup for.
This is done via a protected label that can be accessed in the constructor of the derived user control.
Figure 41:

public VideoVendorVideoServerEditor()
{
InitializeComponent();
// Set the protocol label. protocolLabel.Text = Resources.VideoVendor;
}
Video Server Communication

Once the user enters an IP address into the editor field, the IsUnitPresent override will get called so implementers can try
to connect to their respective video servers. Implementers shall return true if the video server is present and online false if
it is not present. The base class will take care of notifying the user in a generic way that there was a connection error.

There are some protected members that you can use to figure out how to code this overridden. The following code shall be
taken to retrieve the server information that can help with the connection:
// Get the IP address of the server.
IVideoServer server = DataObject as IVideoServer; string videoServerIP = server.IPAddress;
// The credentials to log on to the server. string userName = server.UserID;
string password = server.DecodedPassword;

Import Cameras Button

The video server user interface supports a bulk camera import operation via a button that the user can click. The operation
can only be performed when there is a valid connection to the video server. In other words the button’s enabled state is
tied to the result of the IsUnitPresent override.

When the user clicks on this button the GetAllCameras override is called so implementers can fill in the
SharedVideoCamera list to allow the base class to create all the video cameras in the C•CURE 9000 system accordingly.
The following is an example of what this override might look like:
Protected override IList<SharedVideoCamera> GetAllCameras()
{
// Create a list to hold the cameras in.
IList<SharedVideoCamera> cameras = new List<SharedVideoCamera>();

// Get the cameras using the actual vendor’s SDK and using the
// server information.

// create and add SharedVideoCamera


for (int i = 0; i <= // number of cameras; i++)
{
SharedVideoCamera newCamera = new SharedVideoCamera(); newCamera.Name = // Camera name.
newCamera.Number = i; newCamera.VideoCameraType = // is PTZ ?
VideoCameraType.DomePTZ : VideoCameraType.Fixed; cameras.Add(newCamera);
}
return cameras;
}
Video Camera Client Component
The video server client component will encapsulate the functionality of the video camera client support. A derived class
from VideoCameraClientComponentBase must be generated to encapsulate the video vendor’s functionality.

Overrides

There is only one override that is important and that is the ServerType property. This property shall return the type of video
server the video camera can be connected to. The framework uses this type to do object associations in some parts of the
system. An example of this is the video tree. The video tree uses this association to build the hierarchy and ownership of
the video objects.

The following is a sample of what that derived call shall look like:
[ClientComponent(typeof(VideoVendorVideoCamera),
DefaultVerb=DefaultVerbs.Edit)]
public class VideoVendorCameraClientComponent :

VideoCameraClientComponentBase
{
#region Constructor
/// <summary>
/// Default Constructor
/// </summary>
public VideoVendorClientComponent()
{}
#endregion

#region Create Controls


/// <summary>
/// Edit control to display for the editor.
/// </summary>
/// <value><see cref="VideoVendorComponentEditControl"/> object
/// defining the editor.</value> [ClientComponentEditUI]
public Control ClientComponentEditUI
{
get { return new VideoVendorCameraEditor(); }
}

/// <summary>
/// Viewer control to display for the editor.
/// </summary>
/// <value><see cref="VideoVendorComponentViewControl"/> object
/// defining the viewer.</value> [ClientComponentViewUI]
public Control ClientComponentViewUI
{
get { return new VideoVendorCameraViewer(); }
}
#endregion

#region public overrides


/// <summary>
/// Gets the server type that the camera supports.
/// </summary>
public override Type ServerType
{
get
{
return typeof(VideoVendorVideoServer);
}
}
#endregion
}

Video Camera Editor

To inherit the user interface of the video camera, you must create an inherit user control using Developer studio. When
you add the new item to the project you must select the “inherited user control”. This is the same procedure that was done
for the video server but instead of picking the video server user control to inherit from you must select the
VideoCameraEditControl user control to inherit from.

Once the class has been created the following user interface is inherited (Note that the video control shown in this image
is not inherited, this needs to be added by the implementer):
Figure 42:

Once the inherited user control is created, implementers must drop their video rendering control on the user control. Note
that since the assemblies have to be signed (strong name) the reference or the Active X wrapper reference must also be
generated with a strong name.

The range of the numeric up and down is done by the base class using the totalCameras property of the camera object.
The default is 1-16 if this member is not overridden. If implementers need to manipulate the numeric up and down control
at all they can, accessing it by the numberNUpDn protected member. For example, implementers may want to support
the user changing the number and the video stream changing to reflect the camera number change.

Implementers shall setup their video controls in the OnLoad event handler of the inherit user control. On this event
handler, implementers must call the C•CURE 9000 server to get a reference to the video server object that relates to the
video camera object that is being edited by the editor. This is done using the SDK’s FindObject method as described
below:
// We need the server object that this camera is hooked to
// so we can get the login information
int serverId = (DataObject as VideoCamera).ServerID; VideoServer server =
ClientServerConnection.Instance.
FindObject(typeof(VideoServer), serverId) as VideoServer;
if (server != null)
{
// The server IP address the camera is connected to. string videoServerIP = server.IPAddress;

// The credentials to log on to the server. string userName = server.UserID;


string password = server.DecodedPassword;

// With the IP, user name and password implementers


// must setup the connection.

Video Camera Viewer

The camera viewer is the user control that will support viewing both live and recorded video. In addition is also used for
viewing video tours.

To inherit the user interface of the video camera you must create an inherit user control using Developer studio. When you
add the new item to the project you must select the “inherited user control”. This is the same procedure that was done for
the video server but instead of picking the video server user control to inherit from you must select the
VideoCameraViewerControl user control to inherit from.

Once the class has been created the following user interface is inherited (Note that the video control shown in this image
is not inherited, this needs to be added by the implementer):
Figure 43:

The overrides that need to be implemented are described in the following sections:

Initializations
■ Connect: This method is called when the system needs to establish a connection to start retrieving video from the
video camera. Note that the video server information is retrieved the same way as explained in the video camera
editor section (using the DataObject property).
Implementers shall call Disconnect if there is a problem with the connection or the camera is unreachable at that time.
The base class will show a message to the user indicating the failure.
■ Disconnect: As mentioned above this method is called when the system needs to break the connection to the
camera.
■ VideoControl: The video control protected member must be set in the constructor of the inherited control. This
allows the base class to manipulate the position of it if needed.

Toolbars

There are two toolbars that are shown in the viewer: One toolbar has common video functions and the other one
encapsulates PTZ functions.
Video Functions (non-PTZ)
■ OnShowOverlay: This method is used to toggle the on screen overlay message if applicable. Implementers that do
not want to support this function can access the button via the toolStripButtonShowText member.
■ OnSnapShot: This method is used to take a snapshot of the currently displayed video. Implementers that do not
want to support this function can access the button via the toolStripButtonSnapshot member.
■ OnEmailSnapShot: This method is used to save the snapshot and return the filename of the snapshot saved
■ OnLive/OnRecorded: This is method is used to toggle between live video and recorded video. When the mode is set
to recorded, the recorded video controls are shown on the bottom of the control. The following are the overrides used
in this mode:
■ OnRetrieveVideo: This is what gets the process started. Implementers shall use the parameters of this override
(start time and end time) to retrieve the video that corresponds to that time range. Once that returns the user may use
the record video controls to navigate through the video.
When the video is retrieved, implementers shall also fill in the recordedVideoTotalTime text box with the total time
retrieved. This will show the user the amount of total time that is being retrieved.
■ OnPlay: Called when the video is to be played.
■ OnStop: Called when the video is to be stopped
■ OnPause: Called when the video is to be paused.
■ RecordVideoTrack: The user control supports a track bar to allow the user to navigate through the recorded video
timeline. Implementers must wire event handlers to the recordedVideoTrack member to accomplish this functionality.
As the video is being played, implementers shall give the user some timeline feedback by updating the
recordedVideoCurrentTime text box.
■ OnRateChanged: The user can change the rate of recorded video playback by using the supplied combo box.
This method is called when that happens and allows implementers to adjust the current playback.
■ OnRecord: This method gets called when the user wants to record a video clip using a manual process by clicking on
the record button. Implementers do not need to have special code for this to work since it uses the same mechanisms
that the system uses to record a video clip when an event is triggered. Please refer to the server side section for more
information about how this is done.
■ OnRefresh: The refresh button will call Connect if the video camera is enabled at the time is pressed. If the video
camera is disabled it will call Disconnect.

Video Functions (PTZ)


■ LoadPresets: The load presets override is used to load all the presets supported by the video camera. Implementers
shall return an IList supported collection with SharedPreset objects inside of it. The base class will collect the
information and build the toolbar item with all the presets supported.
■ LoadPatterns: The load patterns override is used to load all the patterns supported by the video camera. To use this,
the ShowPatterns function call has to be called in the OnLive to make the pattern dropdown visible. Implementers
shall return an IList supported collection with SharedPattern objects inside of it.
■ GotoPattern: This override is called when the user has selected one of the patterns in the user interface. The same
SharedPattern object returned to the LoadPatterns method is used to call the actual pattern.
■ GotoPreset: This override is called when the user has selected one of the presets in the user interface. The same
SharedPreset object returned to the LoadPresets method is used to call the actual preset.
■ StartTiltUp: Called when the user presses the up arrow.
■ StartTiltDown: Called when the user presses the down arrow.
■ StopTilt: Called when the user lets go of the mouse on either the up arrow or the down arrow.
■ StartPanLeft: Called when the user presses the left arrow.
■ StartPanRight: Called when the user presses the right arrow.
■ StopPan: Called when the user lets go of the mouse button on either the left arrow or the right arrow.
■ StartZoomIn: Called when the user presses the zoom in button
■ StartZoomOut: Called when the user presses the zoom out button.
■ StopZoom: Called when the user lets go of the mouse button on either the zoom out or the zoom in buttons.

Status Bar

Implementers have access to the status bar when needed by using the statusStrip member.

Server Support for Video


The video server is built by deriving a class from the base class VideoServerComponent and implementing an object
derived from VideoObjectProcessor. This base class contains most of the functionality needed to support the
development of a video server. The video server performs the following tasks:
■ Server connection management
■ Event processing
■ Connection checking (on-line / off-line)
■ Video recording
■ Time Syncing

Sample Code of the Server Component Assembly


The following provides sample code for the Server Support / Server Component and the Server Support / VideoObject
Processor.

Server Support / Server Component


Derive a class from VideoServerComponent and implement the Connect method. In this method there needs to at least
be a creation of your video processor class and a call to the base class as in the following example.

The implementer has to add a reference of SoftwareHouse.CrossFire.Server.VideoServerComponent.dll to the


servercomponent project.
namespace SampleServerComponent
{
public class SampleServerComponent : VideoServerComponent
{
public override void Connect()
{
// Register the types the server component cares about. this.VideoProcessor = new
SampleObjectProcessor();
// Call the base class to do the final connect. base.Connect();
}
}
}

Server Support / VideoObjectProcessor


Derive an object from this class and implement the overrides as necessary. Below is an example of a skeleton version of
an object processor:

public class SampleObjectProcessor : VideoObjectProcessor


{
// SampleServer is the server object and SampleCamera is the
// camera object from the Objects assembly. public SampleObjectProcessor()
: base(typeof(SampleServer), typeof(SampleCamera))
{
}
// Gets called when the driver is shut down. public override void Shutdown()
{
base.Shutdown();
}
// DoServerPoll to Poll the server based on Poll period. protected override bool DoServerPoll
(VideoServer videoServer)
{
return false;
}
// DoCameraPreset to move the camera to a specified preset.
protected override void DoCameraPreset(VideoServer server,VideoCamera camera)
{
}
// Taken care mostly by 9000.
protected override void DoCameraRecord(VideoCamera camera, VideoServer server, int
postAlarmTime, int preAlarmTime)
{
}
// Taken care mostly by 9000.
protected override bool DoEndCameraRecord(VideoCamera camera)
{
return base.DoEndCameraRecord(camera);
}
// DoCameraPattern to move the camera to a specified pattern.
protected override void DoCameraPattern(VideoServer server, VideoCamera camera)
{
}
// Any processing of the alarms needs to written here.
}

Virtual Method Descriptions


The following virtual methods should be implemented in the VideoProcessor object in order to provide specific
functionality to the derived server component. The ip camera type should be specified as null in this release. This is
depicted in the example code above.
■ DoCameraRecord: Record the video (or mark it for safe keeping) as needed so it may be played back at a later time.
■ DoEndCameraRecord: Recording is complete
■ DoServerTimeSync: Set the internal time on the specified server to the time from the system.
■ DoServerPoll: Should return true or false depending upon the connection for each of the server objects created in
C•CURE 9000.
■ DoCameraPattern: Set the camera pattern in the camera specified which is on the server specified.
■ DoCameraPreset: Mover the specified camera to the specified preset.
■ ShutDown: called when the server component is closing. Overrides to this method must call the base class.

Properties
There are properties which the derived classes may set in the base object which will modify the behavior or the system.
These properties are defined as follows:
■ EnableTimeSyncing: The server component object should set this to true to have time syncing handled by the
server and also override the method providing the code to allow syncing. The default value is true.
■ VideoCameraType: The type of cameras supported (can be set in the constructor).
■ videoServerType: The type of the server supported (can be set in the constructor).
Journaling Events
There is a method on the video processor which will allow journaling events encountered in the derived server. Most of
this journaling is handled by the base class. In the case where special journaling is needed the method is
WriteToCameraOverrideAlarmJournalLog.

This method accepts the video camera which is responsible for the event as well as various arguments which describe
the event. Most journaling is already performed by the base class so these methods are included here for completeness
though they may in specific situations be called from the derived classes.

The journaling methods are as follows:


■ WriteToCameraOverrideAlarmJournalLog
■ WriteToCameraJournalLog
■ WriteServerCommStatusToJournal
■ WriteToServerJournalLog
■ WriteToCameraOverrideAlarmJournalLog

This function call is an example of the journaling events.

In this function call, camera is SampleCamera object and CameraMotionBegin is Motion alarm start.
WriteToCameraOverrideAlarmJournalLog(SampleCamera,
JournalLogNetVideoActivityCode.CameraMotionBegin.ToString(), (int)
JournalLogNetVideoActivityCode.CameraMotionBegin, "");

Third-party integration into SOC View


In C•CURE 2.80, a new set of Security Operations Center (SOC) Productivity features was introduced, including a
system to assess events with relevant live and recorded video. C•CURE automatically detects cameras that are
associated with the assessed event, or the user can add and save their own set of cameras, and the system
automatically attempts to retrieve recorded video associated with the time of the event.

The enhanced video grid in SOC Views supports multiple cameras of any type, and shows live and recorded video across
NVRs of multiple manufacturers at once.

To use the most basic aspects of SOC productivity, you do not need to make any changes to an integration. Cameras are
included in C•CURE's algorithmic discovery of associated cameras and can be added to the live camera view by default.

In order to automatically retrieve recorded video and play or pause multiple cameras at once from the SOC View, the third-
party camera must implement a new interface, ISupportRecordedVideoPlaybackControl.

The following image shows a basic Event Assessment viewer, with live video from four different manufacturers.
Figure 44: Event assessment viewer

The following image shows the Recorded Video tab. Two of the four manufacturers support common playback controls,
so their video is automatically retrieved and clicking Play All and Pause All stops and starts their respective playback
controls. The other two do not support the interface and require the user to open the camera manually to retrieve recorded
video.
Figure 45: Recorded Video tab

To command all cameras simultaneously, regardless of manufacturer, use the


ISupportRecordedVideoPlaybackControl interface.

See the following definition of the ISupportRecordedVideoPlaybackControl interface:

The interface is implemented on the video control itself.


NOTE

public interface ISupportRecordedVideoPlaybackControl


{
void RetrieveVideo(DateTime startDateTime, DateTime endDateTime);
event EventHandler<FinishedRetrievingVideoEventArgs> FinishedRetrievingVideo;
void Play();
void Pause();
}

The RetrieveVideo method provides a programmatic hook for asking the control to retrieve recorded video for a
particular timespan. The FinishedRetrievingVideo event should be fired by the control when it finishes recorded
video retrieval, whether successful or not. The Play method causes the video control to resume playback of the video.
The Pause method causes the video control to pause playback of the video.
The following is the definition of the FinishedRetrievingRecordedVideoEventArgs class and the
RetrieveVideoResult enum.
public class FinishedRetrievingVideoEventArgs : EventArgs
{
public FinishedRetrievingVideoEventArgs(RetrieveVideoResult retrieveVideoResult, string message
= "")
{
RetrieveVideoResult = retrieveVideoResult; Message = message;
}
public RetrieveVideoResult RetrieveVideoResult { get; } public string Message { get; }
}
public enum RetrieveVideoResult
{
Success, Fail,
IncompatibleDevice
}

No errors or warnings should be shown as UI elements throughout the video retrieval process. Instead, in case of any
video retrieval failure, invoke the FinishedRetrievingVideo event with FinishedRetrievingVideoEventArgs. If you want to
display a specific message associated with the error in the UI, pass that message to the
FinishedRetrievingVideoEventArgs constructor.

For example:
FinishedRetrievingVideo?.Invoke(this, new FinishedRetrievingVideoEventArgs
(RetrieveVideoResult.Fail, errorMsg));

When video has been successfully retrieved, always invoke the FinishedRetrievingVideo event, with the
FinishedRetrievingVideoEventArgs, passing a parameter of RetrieveVideoResult.Success. No message string is
necessary in this case.

For example:
FinishedRetrievingVideo?.Invoke(this, new FinishedRetrievingVideoEventArgs
(RetrieveVideoResult.Success));

Third-party integration into Video Search


A third-party video integration needs to complete the following steps to get access to the Search Video executor in a video
client component.
Assumptions
C•CURE 9000 provides a way for a third-party integrations to create a set of DLLs to develop an integration with a new
type of video camera. This integration has been available since the v1.0 release of the project.

This document assumes that the integrator has already created such an integration compatible with C•CURE 9000 v2.80,
meaning that there is a DLL that has an implementation of a video client component class (the custom video client
component) that is derived from the class VideoCameraClientComponentBase declared in the namespace
SoftwareHouse.CrossFire.Client.VideoClientComponents.

Enabling Search Video Executor


To make the Search Video executor visible on the context menu for a third-party video camera object, and in other places
the executor is used, modify the custom video client component by completing the following:
■ Override the propertyIsSearchVideoSupported defined in the base class VideoCameraClientComponentBase
and make the property to return the true value.
/// <summary>
/// Allows video search operation on the integration
/// </summary>
public override bool IsSearchVideoSupported => true;
■ Create a new executor class (for example, MySearchVideoExecutor) and substitute the standard implementation of
the Search Video executor with the custom implementation, like the following:
/// <summary>
/// Override RegisterExecutors to Add/Remove executors to the object.
/// </summary>
public override void RegisterExecutors()
{
base.RegisterExecutors();
Executors[SearchVideoExecutor.MenuID] = new MySearchVideoExecutor();
■ It is expected that the Execute(object sender, EventArgs args) of the MySearchVideoExecutor class pops up a form
that provides custom UI for the third-party custom search engine for the selected video camera within the custom
client component (referenced in the property DataObjectKeys or in the property DataObjects of the attached client
component as usual). At this point, there are no formal requirements for the Search Video form invoked by the custom
Search Video executor.
■ It is possible to derive the custom search executor (like MySearchVideoExecutor) from the default video search
executor SearchVideoExecutor defined in the namespace
SoftwareHouse.CrossFire.Client.VideoClientComponents (next to the
VideoCameraClientComponentBase class) .

It provides some help with defining some standard behavior and detecting the IVideoSearchContext value that can be
used in a custom Search Video form (see the description of the IVideoSearchContext interface). If the base class for the
custom Seach Video executor is used, it is necessary to override the protected method CreateSearchVideoForm in
addition to the steps defined in the previous sections:
/// <summary>
/// A derived class can override this method to customize the search
/// form to be displayed to the customer.
/// </summary>
/// <param name="searchContext">
/// Information about the selected video camera and related cameras
/// </param>
/// <returns>A form to be displayed</returns>
protected virtual ContextForm CreateSearchVideoForm( IVideoSearchContext searchContext )
{
// Do not call the base class but create an instance of your custom search form
// Notice that your form must be derived from the ContextForm class. return new
MySearchVideoForm(searchContext);
}

The IVideoSearchContext interface


The interface is defined in the namespace SoftwareHouse.CrossFire.Common.VideoObjectDefinitions
/// <summary>
/// The interface defines the context
/// to be used for the a search video form
/// </summary>
public interface IVideoSearchContext: IContext
{
/// <summary>
/// Video camera to be selected initially
/// </summary>
ObjectKey MainVideoCamera { get; }
/// <summary>
/// An optional list of video cameras related to the main video camera
/// to be used in the search operation
/// </summary>
IEnumerable<ObjectKey> SelectedVideoCameras { get; }
/// <summary>
/// Optional time to be used as the initial time
/// to start streaming the recorded video from
/// </summary>
DateTimeOffset? SelectedTime { get; }
}
The context is provided as a parameter to the CreateSearchVideoForm method of a video search executor. Particular
customization can ignore the parameters in the context (except the MainVideoCamera property that references the
video camera selected by the user).

The property SelectedTime represents the particular time the user is wants to start a recorded stream from. For optimal
performance, start playing the recorded stream on the main video camera from this time (or from few seconds prior to the
specified time) right after the form is opened. If the time is not specified (the property returns null), the form can start
showing the live stream from the selected video camera.

The property SelectedVideoCameras provides references to the video cameras of the same type as the main video
camera. The list is generated from the associations built with the help of the Security View functionality, so all the
referenced video cameras are somehow related to the main video camera. The parameter can be ignored by the form or
the form can allow switching between cameras so the user can observe the recorded stream from each related camera
one by one or simultaneously.

Third-party integration with Clip Management


C•CURE 2.90, introduced a new set of video features, including Clip Management. Clip Management allows for the
download, storage, viewing, and archiving of video clips from a video recorder. This separates the video clips from the
recorder itself, allowing clips to be saved long past the expiration on the recorder. It also allows clips to be easily played in
multiple clients, with viewing rights managed by standard C•CURE privileges. The underlying architecture involves
storing the clips on a network-attached storage location that is accessible by all clients. The file system permissions are
then abstracted away by saving the credentials in System Variables, and then managing access via C•CURE privileges
rather than Windows privileges.

The Clip Management feature is completely open and accessible to third-party partners. There are two main levels of
integration which will allow partners to determine how much of the work should be done in their integration and how much
should be handled by the CrossFire SDK.

Level 1: Using Clip Objects


If a video integration needs to use the new clip objects so that their clips appear in a clips dynamic view and are saved to
the common clip storage location, they can use the Level 1 approach.

With the Level 1 approach you can call into the ClipManagementService and request a new clip object to be created.
Then, the caller can request a file to be persisted to the clip storage and associated with the object. The caller needs to
supply the clip file and its download is handled by the integration.

First, we can examine the ClipManagementArgs. This is a set of values used to initialize a new clip.
public class ClipManagementArgs
{
public string Name { get; set; } = null;
public string Description { get; set; } = null;
public DateTimeOffset? StartDateTime { get; set; } = null; public DateTimeOffset?
EndDateTime { get; set; } = null; public int? CameraId { get; set; } = null;
public int? TimeZoneID { get; set; } = null;
public ObjectKey RelatedObject { get; set; } = null;
}

All parameters are optional, and default values are used where not supplied. For example, if no Name is provided, a
default name will be created using the other given information. StartDateTime and EndDateTime represent the period
of time captured by the video clip. CameraId represents the camera (or the main camera, if more than one camera is
involved) that captured the clip. It should correspond to the ObjectID of a camera in the
ACVSCore.Access.VideoCamera table. TimeZoneID represents the time zone in which the video was captured.
This is needed so that time zone calculations can be made in order to show the user the original time, relative to where the
video was captured, or the local time, relative to the user. This ID should map to the ObjectID of a time zone in the
ACVSCore.dbo.TimeZone table.

Lastly, the RelatedObject property allows any other object in the system to be linked to the clip. This could be a
camera, an event, a door, or another object.

StartDateTime and EndDateTime are used in conjunction with TimeZoneID to perform time zone calculations.
However, since these fields are optional, and since DateTimeOffset may or may not represent the correct time zone,
refer to the following information to fix up these values before saving a new clip.

When determining the TimeZoneID of a clip, if it is not passed in the ClipManagementArgs, use the following
information to pick a time zone. First, check if a camera is given. If so, find its server. If its server has a TimeZoneID,
this becomes the TimeZoneID of the clip. Otherwise, use the local time zone of the CrossFire server. To avoid any
ambiguity, specify the time zone in the ClipManagementArgs.

When saving the StartDateTime and EndDateTime of the clip, if a TimeZoneID has not been specified by the
ClipManagementArgs, save the times exactly as they have been received. They are implicitly converted from
DateTimeOffset to DateTime, so any notion of time zone that is saved in the DateTimeOffset is lost. In order for
this scenario to be acceptable, the times must already be represented in the time zone of the camera or server.

If the TimeZoneID is specified in the ClipManagementArgs, attempt to perform a conversion on the given times to
represent them in the given time zone, so that when they are converted to DateTime and lose time zone info, they will be
correct. If the conversion fails e.g., if the given TimeZoneID does not match any object in the
ACVSCore.dbo.TimeZone table, then the times will be converted as they are to DateTime. In this scenario, the times
must already be represented in the correct time zone, otherwise later conversions will fail.

There are some helper methods in the SoftwareHouse.CrossFire.Common.Objects.TimeZone class to convert


from system TimeZoneInfo, as represented by .NET, to TimeZone objects as represented in the ACVSCore
database.

This method converts a .NET TimeZoneInfo to the TimeZone object.


public static TimeZone GetTimeZoneFromTimeZoneInfo(TimeZoneInfo timeZoneInfo);

This method converts the TimeZone object (via the ObjectID property) to a .NET TimeZoneInfo.
public static TimeZoneInfo GetTimeZoneInfoFromTimeZoneID(int timeZoneID);

This method uses the ObjectID of the TimeZone object to find the object itself. It uses a cache, so it is preferable to a
FindObject call.
public static TimeZone GetTimeZoneFromID(int timeZoneID);
In order to pass the RelatedObject property of the ClipManagementArgs, create an ObjectKey for the related
object. This is straightforward, as the ObjectKey does not need to be populated beyond ObjectID and type. It can be
created in the following way:
ObjectKey relatedObject = new ObjectKey(typeof(Door), 5000);

The following is an example of how to create a clip.


IClip clip = ClipManagementService.Instance.CreateClip(new ClipManagementArgs
{
Name = "Clip from Front Door Camera on 1/1/20",
StartDateTime = DateTimeOffset.Now - TimeSpan.FromMinutes(1),
EndDateTime = DateTimeOffset.Now, CameraId = 5018,
TimeZoneID =
SoftwareHouse.CrossFire.Common.Objects.TimeZone.GetTimeZoneFromTimeZoneIn fo
(TimeZoneInfo.Local).ObjectID,
RelatedObject = new ObjectKey(typeof(XFEvent), 5000)
});

If the clip is created successfully, the CreateClip call returns the populated IClip object with its persisted
ObjectID. At this point, the clip object exists and is accessible to the user in the Clips Dynamic View, but the object has
no knowledge of an underlying clip file that the user can view, and its status is Pending Download.

The next step is to save the file to the common clip management storage location and associate it to the clip. There is a
single call that performs this action. The PersistFileToClip method takes a clip and a path to a file. It copies the file
to the clip management path as configured in the Clip Management Path system variable, updates the clip state to Saved,
and links the saved clip file to the clip object. Optionally, this call can delete the original source file after copying to the clip
storage.
ClipManagementService.Instance.PersistFileToClip(clip, @"path to clip",
deleteSourceFileAfterMove: false);

The user of the client who calls PersistFileToClip must have read permissions to the original clip file location (or
write permissions, if the deleteSourceFileAfterMove is true). Permissions to the destination location are handled
by the Clip Management Alternate Credentials System Variable.

At this point, the clip file has been linked to the object, so the user can watch the clip by right-clicking on the clip object
and selecting View.

If the clip object has an associated CameraId, the clip player attempts to play the clip in the video integration’s clip
player. This requires an additional level of integration. If the video clip can be played in a standard media player, it is not
necessary for the video integration to implement a clip player. The following message displays when a user attempts to
play a clip:

The camera does not support clip playback. Would you like to try and play the clip
with the built-in video player?

To avoid this message complete one of the following:


■ Do not set a CameraID on the clip, instead set up the camera as a RelatedObject.
■ Implement the clip player.

Level 2: Allowing Automatic Clip Downloads


A partner can allow their video integration to be one of the available options when the user is creating an automatic clip
download via an event action. In addition, if the video partner supplies a custom video file that cannot be played in a
standard media player, they need to implement a clip player control.

A user may configure an event that automatically downloads and saves a clip. In the Event editor, Action tab, add an
action of type Video Camera Action. Pick a server and camera, switch to the Action Type tab, and select Record Video.
At the bottom of the action configuration, there is an option to save recording to clip. This option is enabled or disabled
depending on whether the selected video server supports clip management integration. To enable this option, the video
server type must implement IRetrieveClipFactory. This interface defines a method for returning an IRetrieveClip, which
can be implemented by any type in the integration and downloads the clip. See the following interface definitions.
public interface IRetrieveClipFactory
{
IRetrieveClip GetClipRetriever();
}
public interface IRetrieveClip
{
void RetrieveClip(IClip clip); bool ReportsProgress { get; }
event EventHandler<FinishedRetrievingClipEventArgs> FinishedRetrievingClip;
event EventHandler<ClipRetrievalProgressEventArgs> ClipRetrievalProgress;
}

Any type in the video integration can implement IRetrieveClip, although it is expected that each retrieval will occur in
a new instance of this type, since the retriever will fire events related to the progress of a single clip download. The
download is initiated when you call RetrieveClip on your retriever class. The IClip object that is passed is a
persistent object in the database and should contain all of the necessary information needed to perform a download, such
as the CameraId, StartDateTime, EndDateTime,and TimeZoneID. The retriever should declare that
ReportsProgress is true if it ever intends to invoke ClipRetrievalProgress. The Progress property of the
ClipRetrievalProgressEventArgs should be a whole number which represents a percentage out of 100. For
example, if the value 50 is passed in the args, the client will show that the clip has downloaded at 50%.

The server listens for progress changes and persists them at a reasonable rate, meaning that not every progress update is
necessarily reflected in the client, but it also prevents the server from getting overloaded by updates.

Finally, when the clip has finished downloading or fails to download, invoke the FinishedRetrievingClip event. If
successful, the FinishedRetrievingClipEventArgs.Result property should be
FinishedRetrievingClipResult.Success, and the FinishedRetrievingClipEventArgs.Location
should point to the video file that has been downloaded by the integration. If it fails, the
FinishedRetrievingClipEventArgs.Result property should be set to one of the other
FinishedRetrievingClipResult items that most closely matches the given scenario.
Unlike manually creating a clip, in this case the integration does not need to call PersistFileToClip, since invoking
the FinishedRetrievingClipEventArgs with a successful result causes the server to perform the file copy. The
original file attempts to be deleted by the server after being copied to the clip management storage location.

Here is an example of an IRetrieveClip implementation.


public class MyClipDownloader : IRetrieveClip
{
public void RetrieveClip(IClip clip)
{
int cameraId = clip.CameraID;
DateTime clipStartDateTime = clip.StartDateTime; DateTime clipEndDateTime =
clip.EndDateTime;
// TODO: Add logic to perform a clip downloading using the information above
// Simulate some progress
ClipRetrievalProgress?.Invoke(this, new ClipRetrievalProgressEventArgs
{ClipId = clip.ObjectID, Progress = 50});
// if successful
FinishedRetrievingClip?.Invoke(this, new FinishedRetrievingClipEventArgs
{ClipId = clip.ObjectID, Result = FinishedRetrievingClipResult.Success, Location =
"path to clip"};
// else if unsuccessful
FinishedRetrievingClip?.Invoke(this, new FinishedRetrievingClipEventArgs
{ ClipId = clip.ObjectID, Result = FinishedRetrievingClipResult.FailureNoMedia};
}

public bool ReportsProgress { get; } = true;


public event EventHandler<FinishedRetrievingClipEventArgs> FinishedRetrievingClip;
public event EventHandler<ClipRetrievalProgressEventArgs> ClipRetrievalProgress;
}

This is all that is needed for inclusion in the automatic clip download section. If the video integration uses a proprietary
video format that cannot be played in a standard video player, a control needs to be supplied which can play the video
files. In order to do this, the video integration’s ClientComponent should implement
IPlaybackClipFactory<Control>. As with the IRetrieveClipFactory, the IPlaybackClipFactory
exists to instantiate a new control that can play a video clip. The control should implement IPlaybackClip. See the
following definition of the interfaces.
public interface IPlaybackClipFactory<out PlaybackControlBaseType>
{
IPlaybackClip<PlaybackControlBaseType> GetClipPlayer();
}
public interface IPlaybackClip<out PlaybackControlBaseType>
{
PlaybackControlBaseType PlaybackControl { get; } IClip ClipToPlayback { get; set; }
}

Although both interfaces are generic, they should use the WinForms Control class as their type. See the following
example of how these classes are invoked from the code.

The ClientComponent doubles a factory that creates the viewer control, then the viewer control is added to the forms. The viewer
NOTE control is commanded to play the clip by setting the ClipToPlayback property. The video partner’s implementation of the setter of
that property should include logic that loads the clip and optionally begins playback.

var videoCameraClientComponent = ClientComponentsManager.GetComponent


(cameraObjectKey.ClassType);
if (videoCameraClientComponent is IPlaybackClipFactory<Control>
playbackClipFactory)
{
var clipPlayer = playbackClipFactory.GetClipPlayer(); Control clipPlayerControl =
clipPlayer.PlaybackControl; Controls.Add(clipPlayerControl);
clipPlayer.ClipToPlayback = _clip;
}

See the following example implementation of IPlaybackClipFactory and IPlaybackClip.


public class MyVideoClientComponent : IClientComponent,
IPlaybackClipFactory<Control>
{
public IPlaybackClip<Control> GetClipPlayer() => new MyClipPlayer();

// ... IClientComponent members ...


}
public class MyClipPlayer : IPlaybackClip<Control>
{
public Control PlaybackControl
{
get
{
// TODO: Construct a control that can play clips from my video integration
return new Control();
}
}

public IClip ClipToPlayback


{
get => _clip set
{
_clip = value;
string clipLocation = _clip.Location;
// TODO: Load the clip from the location above
}
}
private IClip _clip;
}

Use the ClipManagement trace switch for tracing clip management events in the system trace log. This call is always
redirected to the server, so whether it is called from the client or server, the message ends up in the server’s trace log.
See the following example.
ClipManagementService.Instance.TraceMessage(TraceLevel.Info, $"My video integration
began downloading file for clip object {clip.ObjectID}");

Video Feature Support


Features highlighted in the following tables are supported in recent versions of C•CURE 9000 starting from v2.80.
NOTE

Table 2: Configuration features

Configuration features Coding level

Video Server object

Configure (add/edit/delete) Standard

Support Time Zone Standard

Allow to customize communication/ live/ alarm ports Standard

Support local authentication (UN/PW) Standard

Support Secure Connection (https...) Extended +


PVFR

Support DNS Standard

Allow to customize polling period Standard

Allow to customize Comm Fail Delay Standard

Support Time Sync Standard

Support the adjustment of recording frame rate Standard

Allow to import cameras Extended


Configuration features Coding level

Allow to configure event trigger on server alarms Standard

Allow to configure camera video action trigger on server alarms (Comm Loss, Comm Restore, Storage Status) Standard

Allow to configure camera alarm triggers on schedule Standard

Import object PlatformReady

Features from the Context menu: View Standard

- Popup view Standard

- View in current tab Standard

- Set property PlatformReady

- Add to group PlatformReady

- Export object PlatformReady

- Find in audit PlatformReady

- Find in journal PlatformReady

Configuration features Coding level

- Set GIS location PlatformReady

- Associate Tag PlatformReady

- Associations PlatformReady

Camera object

Add/Edit/Delete Standard

Allow to select camera type Standard

Allow to specify camera number Standard

Allow to view live video from the camera during editing the configuration Extended

Allow to display camera in full screen Extended

Allow to configure event trigger on camera alarms (Motion, video loss) Standard

Allow to configure camera video action trigger on camera alarms Standard

Allow to configure camera alarm triggers on schedule Standard

Features from the Context menu: View Standard

- Popup view Standard


Configuration features Coding level

- View in current tab Standard

- Set property PlatformReady

- Add to group PlatformReady

- Export object PlatformReady

- Find in audit PlatformReady

- Find in journal PlatformReady

- Set GIS Location PlatformReady

- Associate Tag PlatformReady

- Associations PlatformReady

- Monitor PlatformReady

Others

Video Tour PlatformReady

Configuration features Coding level

Video View (1x1, 2x2,...4x4...) PlatformReady

Security View/Assess Event - Assess Live Videos (view the video from the cameras that are closest to the alarm source or PlatformReady
incident)

Security View/Assess Event - Assess Recorded Videos Extended

Security View/Assess Event - Discover Security Videos PlatformReady

Security View/Assess Event - Map Location of alarm sources and access standard operating procedures (SOPs) to process PlatformReady
the incident the way your system requires

Clip Management (Central Clip Storage, Screen Recordings, Auto Capture Clips on Alarm…): use C•CURE clip objects within Extended
partner integration

Clip Management (Central Clip Storage, Screen Recordings, Auto Capture Clips on Alarm…): allow C•CURE to automatically Extended
create partner clips outside integration (e.g., events)

Table 3: Video monitoring features

Video monitoring features Coding level

Live video

View live video from C•CURE Admin - in a new tab Standard


Video monitoring features Coding level

View live videofrom C•CURE Admin - via a popup view Standard

View live video from C•CURE Monitoring Station - in a popup view Standard

View live video from a single camera Standard

View live video from a pre-defined view (grid view/video wall) PlatformReady

View live video from a video tour PlatformReady

Actions from the live video player toolbar: Show text overlay Extended + PVFR

- Audio Muter/ Unmute Extended + PVFR

- Take a snapshot Extended

- Save snapshot to a file Extended

- Email the snapshot Extended

- Record video Extended + PVFR

- Retrieve a recorded video clip with Start/ End date time Extended + PVFR

- Refresh the connection Extended

Video monitoring features Coding level

- Use features on the PTZ control for Dome cameras Extended

- Maximise the player PlatformReady

- Restore the player PlatformReady

Recorded video

View recorded video from Administration window Standard + PVFR

View recorded video from Monitoring Station window Standard + PVFR

View recorded video from the journal messages on the Activity Viewer window Standard + PVFR

View recorded video from the journal messages on the Journal Replay Result window Standard + PVFR

Video Search Extended + PVFR

View recorded video from a single camera Standard + PVFR

View recorded videos from multiple cameras Extended + PVFR

Save recorded video clip to a file Extended + PVFR

Email the recorded video clip Extended


Video monitoring features Coding level

Get live video from the recorded video player Extended

Use features on the recorded video control bar (Play/Pause/Stop, Fast Forward/Backward…) PVFR

Use features on the recorded video image tool (Zoom, bright/contrast, full screen, save, print...) PVFR

Actions

Record video on camera with Start/ End date time Extended + PVFR

Send camera preset command Extended + PVFR

Send camera pattern command Extended + PVFR

Table 4: Other features

Other features Coding level

Video server support On-Demand recording PVFR

Auditing to all server/camera objects PlatformReady

Journal messages for video activities Standard

Original recorded video cannot be altered PVFR

Support the placing of video object icons on a map PlatformReady

Show video object status on a map Standard +


PlatformReady

Show video object status on dynamic views Standard +


PlatformReady

Support all video features above on C•CURE 9000 Standalone Server Standard

Support all video features above on C•CURE 9000 Satellite Server Standard

Video Central Monitoring: Support all video features above on remote client connected to C•CURE 9000 Master Standard
Application Server

Support all video features above on Stratus - High Availability (HA) setup PlatformReady

Support all video features above on ArcServ - Disaster Recovery (DR) setup Extended
Coding Level

■ Standard: Expected functionality for a third-party integration that is based on Video framework. Needs normal
custom coding to make the feature integrated with NVR support.
■ Extended: Additional functionality that makes the Video feature complete, might require extra effort to make it work
and integrated with NVR.
■ PVFR: Partner Video Feature Ready. It means that support of this functionality depends on the NVR video server
implementation significantly.
■ PlatformReady: The feature is available as part of C•CURE 9000 platform. Minimal or no addition code development
is needed if the rest of the integration code is designed properly.

It is the partner’s responsibility to develop and test to ensure video features function properly.

To use the Video Central Monitoring features when connecting remote clients to the MAS, the following conditions must
be followed:
■ All video integration components are installed normally on SASs and remote clients connected to SASs as they are
installed on a Standalone system.
■ The video integration driver services must not be installed on the MAS.
■ The video integration does not add any new tables in the C•CURE 9000 database but use existing video objects
tables in the C•CURE 9000 database.
■ The video integration common DLLs should be installed on the MAS server and the remote clients that connect to the
MAS.
Hardware Framework
This chapter describes the C•CURE 9000 Hardware Framework and explains how to build components to integrate
hardware into a Connected Program product.

The hardware framework allows integrators to incorporate hardware related object management in the Hardware pane of
the C•CURE 9000 administration application. In addition, it hides any required knowledge needed with regards of .NET
Tree control manipulation and management.

The framework is predicated on the notion that hardware objects are usually managed via a parent-child relationship. For
example, in access control, controllers own I/O boards which own end points like inputs and outputs. The parent of the
inputs and outputs is a board and the parent of it is a controller

By establishing these relationships, the framework is able to describe and present to the user sensible data.

See Adding Your Component to the Hardware Tree for information about making you integration visible in the Hardware
pane.

See Client Support for Hardware on Page 148 for more information on Client Components. See Server Support for
Hardware on Page 152 for more information on Server Components.

Adding Your Component to the Hardware Tree


If you want you integration component to appear in the Hardware Tree and Hardware Pane drop-down list, you need to do
the following:
Table 5:

Step See...

1. Derive a class for the integration from Client Support for Hardware on
SoftwareHouse.CrossFire.Client.NavigationFramework.CrossFireTreeExtender Page 148

2. License your integration with Software House. How to update C•CURE 9000
license to enable the integration
license on Page 166

3. To ensure that your objects only appear in the Hardware tree when correctly License Check on Page 148
licensed by the customer, you can add a licensing check to .your
CrossFireTreeExtender class.
Configure Custom Hardware

License Check
You can include a license check, based on the code example below, in your CrossFireTreeExtender class to ensure that
your objects only appear in the Hardware tree when correctly licensed by the customer.

Substitute your GUID in the code example.


/// <summary>
/// Is OSI Licensed
/// </summary>
private bool IsOSILicensed
{
get
{
Guid? g = new Guid( "04e93788-19eb-47c3-9111-459ff5bd9c37" );
ServerResult serverResult = ClientServerConnection.Instance.ServiceRequest(
typeof( IConsentAuthority ).FullName,
"IsOptionLicensed", new object[ ] { g.Value } );
return ( ( bool? )serverResult.OperationResult ?? false );
}
}

Client Support for Hardware


Client support for hardware is based on extending the Hardware Tree to include your integration's hardware objects. The
hardware tree is a tree control that loads data "on demand" from user’s interaction. "On demand" means that the tree
control does not load any data that the user has not requested. When the user expands a particular container, the tree
makes the needed requests to load the objects underneath that container. This happens at every level of the tree for all
types of containers.

The following sections describe the different parts of the hardware tree and how implementers would do to extend them.
The following is an image of the hardware tree and all its parts:
Root: The root of the tree is the main container that holds all the objects. Usually objects that do not require parents are
created at this level.

Class Folder: Class folders are special containers that are made to keep objects of the same type together and well
organized. Implementers have the option of having these folders or not. This will be explained in the
CrossFireTreeExtender section of this document.

Hardware Folder: Hardware folders are containers that are created by the user and they are used to organize hardware
objects depending on the user’s requirements. These folders can hold many different types of objects which can be
controlled by implementers.

In addition, drag and drop operations are supported for objects that are one level under a hardware folder. In other words,
objects whose parent is a hardware folder may be dragged and drop in another hardware folder.

CrossFireTreeExtender class
The key to the integration is done by deriving a class from
SoftwareHouse.CrossFire.Client.NavigationFramework.CrossFireTreeExtender and overriding the required methods
depending on your requirements. The hardware tree at startup looks up all the derived classes and uses those objects
from them on to build the tree as well as to describe its behavior.
References

The required references are the following:


■ SoftwareHouse.CrossFire.Client.NavigationFramework.dll
■ SoftwareHouse.CrossFire.Client.ClientComponentsFramework.dll
■ SoftwareHouse.CrossFire.Common.Interfaces.dll
■ SoftwareHouse.CrossFire.Client.Core.dll

Overridable Methods

The following is the list of virtual methods that implementers can override.

SupportedTypesForNode

public override Type[] SupportedTypesForNode(Type node)

This method controls what type of objects are loaded by the hardware tree at each level. This method is called by the tree
control when it needs the types of objects that it needs to load at each level. Implementers shall return an array of types
that are supported at each particular level of the tree. Two things to keep in mind, the first is that a value of null for the
arguments means that the tree is loading its root. The second, being that you should always include a case for a node of
type HardwareFolder The following is an example of what an implementation might look like:
if (node == null)
return new Type[] { typeof(CommPort), typeof(CommPortApc),
typeof(DigitalCertificate), typeof(HardwareFolder), typeof(Floor) };

else if (node == typeof(HardwareFolder))


return new Type[] { typeof(Cluster),
typeof(ISCController), typeof(ApcController) };

else if (node == typeof(iStarController))


return new Type[] { typeof(iStarComBoard),
typeof(iStarACMBoard),
typeof(iStarInput), typeof(Output),
typeof(iStarDoor), typeof(Elevator),
typeof(iStarReader) };

SupportedTypesForCreation

public override Type[] SupportedTypesForCreation (Type node)


This method controls what types are allowed to be created under any specific node. Implementers shall return the types
that the user may be able to create at the respective node levels. The tree control uses the valid types for creation to figure
out the state of context menus as well as the “New” button that is located at the top of it. Usually the implementations are
very similar than the ones made in the SupportedTypesForNode override, with the difference that the types returned are
used for new objects.

Note that if a type is valid to be created at a particular level, its template type shall also be allowed. The hardware tree will
treat them both as either valid or invalid.

ClassFoldersImageList

public override ImageList ClassFoldersImageList

Class folders that show up in the tree are controlled by this override. The tree needs an image list that includes all the
class folders that shall be supported by the extender. Note, that if the tree does not find a class folder image it will not
create a class folder for the respective type. The following is an example of what an implementation might look like:
get
{
ImageList imageList = new ImageList();
imageList.ColorDepth = ColorDepth.Depth32Bit;
imageList.Images.Add(typeof(iStarInput).Name + "_Folder",
Resources.Input_Folder);
imageList.Images.Add(typeof(iStarReader).Name +
"_Folder", Resources.Reader_folder);
imageList.Images.Add(typeof(iStarDoor).Name +
"_Folder", Resources.Door_folder);
return imageList;
}

You may notice that the keys of the icons are always followed by “_Folder”. This is done so the tree distinguishes an
image that has been added by an extenders with one that is internal for the use of the tree control.

DisplayTextForClassFolder

public override string DisplayTextForClassFolder(Type node)

This method shall return the text used for a particular class folder. Usually a class folders text is different than the name of
the type itself to indicate that the folder holds more than one object. For example, a class folder of Doors is shown as
“Doors” and not just “Door”. Use this override to indicate what the tree shall show the user for the text of the class folder. If
a class folder exists and there is no text for it (meaning that this method does not include an entry for that type) the tree
control uses just the class name for it’s the folder (ex: Door). The following is an example of what an implementation might
look like:
if (node == typeof(Output))
return Resources.Outputs;
return string.Empty;

OrphanTypes

public override Type[] OrphanTypes()

The tree control supports displaying objects that require parents but that do not have a valid parent at the time the tree is
built. To do that, it must know which types shall be allowed to be displayed or not. Some implementers decide not to
display the objects when they are in this state, some do. This method shall return the allowed types that can be orphan.
When the tree detects this condition it adds the objects to a folder called “Unassigned” so the user can identify if this is a
problem or not.

Server Support for Hardware


The data service objects that participate in the hardware framework must implement the IHardwareObject interface
defined in the SoftwareHouse.CrossFire.Common.Core.dll assembly. This interface defines the relationships between
hardware objects.

IHardwareObject interface
The following are the list of properties and methods that are included in this interface.
■ ParentObjectType: This member shall return the System.Type of the parent object instance that the interface is
implemented for.
■ ParentObjectID: This member shall return the int of the parent object instance that the interface is implemented for.
Samples, Tools, and Templates
This chapter describes the Samples, Tools, and Templates provided with the Connected Program Kit. In this chapter
■ Samples
■ Tools
■ Templates
■ ACVS DB Object Editor Overview

Samples
The Connected Program Kit provides sample integration projects via the CrossFireSamples.sln, located in the
\Tyco\CrossFire\SampleSource directory.

CrossFireSamples Solution
The CrossFireSamples Visual Studio Solution is composed of the following directories and sample projects.
Figure 46:

ACKEventSample

ACKEventServer. The ACKEventServer is a sample C•CURE 9000 ServerComponent which demonstrates how to listen
for XFEvents and how to perform the necessary ManualActions to acknowledge these XFEvents.
If this ServerComponent is started, all events in C•CURE 9000 will automatically be acknowledged.
NOTE

CrossFireSample

■ ClientComponent: In the CrossFireSample folder there are two client component implementations. One of them
shows the basics of what a client component is and how it is used. The second one has actual interaction with a
server component.
• clientComponent itself has an editor that exercises the basics of a client component as well as most of the user
controls exposed by the control library. Implementers creating their own editors should use this sample as a
reference for their own editors.
• ClientComponentExcelClientComponent shows how changing values of properties on the client can interact with
a particular server component. Refer to the SampleServerComponentExcel class. This client component has both
an editor and a view. The view will update dynamically if any of the properties on it change. Use this sample as a
base when there is need of client-to-server updates as well as server-to-client updates.
■ ClientNavigation: There are two sample navigation classes in this folder. One of them shows a basic navigation
pane and the other shows a custom one.
• SamplePane1. This pane shows how to create a new navigation pane using all the basic features supported.
• SamplePane2. This pane shows how to create a new navigation pane using a custom control and all the advanced
features supported.
■ SampleObjects: This is a sample project that demonstrates how to build custom objects in C•CURE 9000.
■ SampleServerComponent: The sample server component can be used as a base line for other sample components
being implemented. It exercises most of the most common operations that would be implemented as extensions to
the C•CURE 9000. The sample components provided can be exercised in the sample client application provided. The
client/server interaction can show you how implementers can create dynamic behaviors inside the C•CURE 9000
system.

CustomActionSample
The Sample consists of two projects.
■ CustomActionClient. Contains the Action as it would be configured in your Client component.
■ CustomActionObject. Contains the Action as it would be configured in your Objects assembly.

CustomeJournalMessageSample
The Sample consists of two projects.
■ CustomeJournalMessageSample: This sample demonstrates how users can construct their own journal message
activity, which displays in the Monitoring Station Activity Viewer and gets stored in the journal database. The
CustomeJournalMessageSample.dll should be placed in the Tyco\CrossFire and Tyco\CCURE Client folders. Once
the CustomeJournalMessageSample.dll is placed in these folders, restart both the CrossFire Framework Service and
the CrossFire Server Component Framework Service from the Server Configuration Application.
■ JournalSampleTest: The JournalSampleTest.exe should be placed in the Tyco\CCURE Client folder. After building
the CustomeJournalMessageSample.dll and restarting both CrossFire Services, start up the Monitoring Station. Run
the JournalSampleTest.exe and a custom log message displays in the Monitoring station.
Figure 47: Journal message

In Summary, the CustomeJournalMessageSample is comprised of the following procedure:


1. Create a CustomJournalMessageFormatBase class that extends
the SoftwareHouse.CrossFire.Common.DataServiceLayer.MessageFormatBase class.
• You can override the FormatKey string with your custom key whose value is set in the Resources.resx
• You can also override the DefaultFormatString to match the message composition format you would like to see
reflected in the Monitoring Station.
• You can override the FormatTagData[] MessageItemTags to align with the values you will pass to
the custom message printed in the Monitoring Station. The tag values will be defined in the Resource file.
2. You need to develop a CustomJournalMessage class that extends
the SoftwareHouse.CrossFire.Common.Shared.JournalLogTypeBase class.
• Override the FormatBaseType, to return the CustomJournalMessageFormatBase you have created with
your customizations.
3. Configure a Runnable program that connects to the CrossFire Server and uses
SoftwareHouse.CrossFire.Common.Objects to construct a Journaling4 object that takes
your CustomJournalMessage type as a parameter. Define the Server and Message DateTime and UTC values for the
message with the current time. The Persist Option and MessageCodes for each of the CustomTags should be set up
in the Journaling4 object. The program should then send the configured journal message to the Journal.

CustomTriggerSample
SampleVideoCamera. This sample demonstrates how to add a custom trigger (AudioLoss) to a video camera. By default,
Video Cameras have four alarms (Motion, Analytics, Normal, Alarm). This sample shows how to add properties to the
videocamera to create a custom trigger.

JournalNotificationSample
■ Message Proxy: The sample project demonstrates how to register for MessageProxyEvent callbacks and parse data
sent from the callback.

PersonnelExtSamples
■ CustomPersonnelControl.Client.Component: This project contains the two core components of a personnel
framework integration – a client component (CCURE personnel GUI integration) along with an underlying control
(business logic, form elements). In this example, the client component hosts an instance of a control, which merely
imports a WPF button and a text field (whose contents can be stored and retrieved using the data mechanism
described above).
■ CustomPersonnelControlData: This project contains an example of the data-source portion of the personnel
framework which directly corresponds to the values used in the CustomPersonnelControlSample.Client.Component
project. The CustomWPFPersonnelControlData.cs class contains a property named “String1”, which is the holding-
place for data entered through the “String1” form in the control. CustomPersonnelControl.cs is responsible for
serializing and deserializing this class in order to access the contents of String1. In this case, String1 is just an
example. That property could be transformed into a placeholder for any pertinent data that is required to be stored for
future lookups.

TableDerivationSample
■ TableDerivationObjects: TableDerivationObjects is a sample project which demonstrates how to build Objects
which derive from existing Objects in C•CURE 9000. This particular example derives and expands the existing
Objects sample project.
■ TableDerivationClient: TableDerivationClient is a sample C•CURE 9000 Client Component which demonstrates
how to design C•CURE 9000 Client Component using derived Objects.
■ TableDerivationNavigation: The TableDerivationNavigation is a sample C•CURE 9000 Navigation Pane which
demonstrates how to design a C•CURE 9000 Navigation Pane using derived Objects.

UDFSample
UDFSample: UDFSample displays how to get all UDF from the system. It states all the UDF properties of Personnel
Objects, allows the user update these properties and delete specific UDF from the system.

Tools
The Software House Connected Program Kit comes bundled with a sample Visual C# project, Visual Studio templates,
and tools to test assemblies.

Starting in version 2.90, the enabled Partner DLL Signature Passage development license option allows you to integrate
unsigned assemblies with the Administration Workstation in the development environment. This requires that you only
submit your assemblies to the SDK Engineering team for strong signing after your assemblies are fully tested in
development and are ready to release to production.

You do not need to use the ServerComponentTestHost to validate SereverComponent load and startup. To install a
signed or unsigned ServerComponent DriverService, run the Command Prompt window as an administrator and enter the
following:

"C:\Windows\Microsoft.NET\Framework\v4.0.30319\installutil.exe" "C:\Program Files


(x86)\Tyco\CrossFire\ServerComponents\YourDriverServiceFullName.exe"

Unsigned Server Component Assemblies can be installed and loaded in the Server Configuration Application, however
this will trigger an Invalid Service error as shown in the following image.
Figure 48: Invalid service error

As a workaround, start up the integration driver service through the Windows Services application. The Server
Configuration Application reflects that your integration driver service is running.
Figure 49: Server Configuration application

Legacy Testing Tools


For the current Partner Kit Release the Legacy Testing tools continue to be available, but it is advised to use the
aforementioned testing methods.

Sample Client (SampleConfiguration.exe)

SampleConfiguration is a special C•CURE 9000 administration that can load unsigned client components and is to be
used during development and testing.

Sample Configuration can be accessed at this directory: C:\Program Files (x86)\Tyco\CCure Client\
Figure 50:

Sample Server (ServerComponentTestHost.exe)

ServerComponentTestHost is a legacy tool that loads unsigned server components into CrossFire. It is advised instead
to test unsigned ServerComponent Services in the Server Configuration Application, by installing the Windows Service
and starting it up through the Windows Services Application.

This test host will be removed in the next version of the Connected Partner Kit.
NOTE

Templates
The Templates shown in Templates in C•CURE 9000 SDK are included with the C•CURE 9000 SDK and get installed to
the following directory:
C:\Users\[username]\Documents\Visual Studio 2019\Templates\ProjectTemplates\Visual C#

When generating components using these templates, remember to change the Target .NET Framework to .Net
Framework 4.8.
Figure 51: Templates in C•CURE 9000 SDK

SampleVideoClient
SampleVideoClient is a C# Visual Studio Template which helps you create a C-Cure Client Component.

SampleVideoObjects
SampleVideoObjects is a C# Visual Studio Template which helps you create C-Cure Video objects.

SampleVideoServerComponentService
This template is the recommended approach for creating a VideoServerComponent. Partners should no longer use the
non- service VideoServerComponent.

Partners can still use SampleComponentTestHost.exe or ServerConfiguration application to test this type of project.
Partners need to use Installutil to install this service.

See http://msdn.microsoft.com/en-us/library/50614e95(v=vs.100).aspx for more information.

The Installutil post-build script is useful for re-installing the service after changes are made in Visual Studio:
$(WINDIR)\Microsoft.NET\Framework\v4.0.30319\installutil.exe /u $(TargetPath)
$(WINDIR)\Microsoft.NET\Framework\v4.0.30319\installutil.exe $(TargetPath)
Make sure to set the Application Target Framework for each Template Project to .NET Framework 4.8. In Visual Studio, right click
NOTE on each template project in the Solution Explorer and select Properties. In the Application tab, select .NET Framework 4.8 from the
Target Framework list.
The v3.0 Connected Partner Package download’s ‘CCURE_CPK_Partner_ISO’ folder contains a SampleVideoIntegration
subfolder with a functioning VideoTreeExtenderSample.sln built using the aforementioned Video Templates and re-targeted for
compatibility with .NET Framework 4.8. Said sample can be used as a Video Integration development reference instead of building
a new integration project using the foregoing templates.
Developing for the Enterprise Architecture
This chapter describes the integration of a C•CURE 9000 Connected Program product into an Enterprise Architecture
configuration.

In this chapter:
■ Integrating with the Enterprise Architecture Option
■ Discovery/Reporting Tool

Integrating with the Enterprise Architecture Option

Object Validation Code


Object ‘locality’ should always be set to local-only, that is what it is by default. For local-only objects which are not part of
hardware or video tree, validation needs to be added to ensure that new objects are created in a valid partition.

Disabling Replication

URE 9000 2.0 or later uses Microsoft Synchronization Framework to sync or replicate data from the SAS to the MAS. By
default, all SDK objects which have a persistence class and either a Partition ID or a Foreign key reference to a table
which has a Partition ID will be replicated.

Because Connected Program Kit Integrations do not support synchronization, It is important that all tables in your
integration must be added to the “IgnoreSyncTables” list in the Server SoftwareHouse.CrossFire.Server.exe.config
configuration file so that they are ignored for synchronization purposes.

IgnoreSyncTables is only useful to DEV and is ignored in customer installs.


NOTE

<?xml version="1.0" encoding="utf-8"?>


<configuration>
<appSettings>
<add key="MinimumCollectionSizeToPage" value="2500"/>
<add key="MaximumPageSize" value="2500"/>
<add key="TransactionTimeoutInSeconds" value="300"/>
<add key="DefaultLoggingLevel" value="Error"/>
<add key="MaximumDebugLogMessageSizeInMB" value="50"/>
<add key="MaximumNumberOfLogFiles" value="10"/>
<add key="PlatformDatabaseVersion" value="0.0.400.0008"/>
<add key="ProductDatabaseVersion" value="0.0.400.0009"/>
<add key="StartServerComponentFramework" value="0"/>
<add key="IgnoreSyncTables" value="SampleObject1,SampleObject2" />
<add key="IgnoreSyncDB" value="WAMS,SWHSystemAudit,SWHSystemJournal"/>
<add key="OrderSyncTables" value="UdfFieldDef" />
</appSettings>
<connectionStrings>
</connectionStrings>

Resolving Mismatches
It is important that the Object’s persistence property attributes line up with the attributes of the corresponding DB table
column. For example, if a persistence property is marked as Nullable in the property definition, its corresponding column
should also be marked as Nullable.

Class definition:
[PersistentProperty("Description", DbType.String, Size=500, Nullable=true, IsIndex=false)]

Integrating with the Enterprise Architecture Option


Table Definition:
[Description] [dbo].[Description] NULL,

The Discovery/Reporting Tool can be used to detect these mismatches.

Please note that the Synchronizable/Comparison Report and all Sync columns are for Software House use only.
NOTE

Discovery/Reporting Tool
The C•CURE 9000 tool DiscoverySyncStatusTool.exe has a Comparison Report that is useful for determining if the
persistence properties of objects are correctly matched with the corresponding table column attributes. The Comparison
Report lists all of the objects from assemblies you specify that have mismatches between the PersistentProperty and the
table definition.

The C•CURE 9000 DiscoverySyncStatusTool is installed with the Connected Program Kit in the Tyco\CrossFire
directory where C•CURE 9000 is installed.

Running the DiscoverySyncStatus Tool


1. Select Start > Programs > Software House > Platform SDK > Tools > Discovery Sync Status Tool.
2. Click and navigate to the Tyco\CrossFire directory.
3. Click Load Assemblies.
4. When the list of assemblies appears, select the assemblies you wish to search and click Create Report.

5. Click the Database Comparison Report tab to view the report.

6. You can use the report results to identify and fix any objects in your integration that have mismatched properties.
7. You can also save the report results in a .CSV file by clicking Save Report.
Appendices

How to apply for an integration GUID


A licensed GUID (ClientID) is required for C•CURE Integration Assemblies to be accepted for integration to CrossFire.
Request the Product Management team create and register a GUID for your integration. You will receive a license file with
your integration option and GUID enabled, along with a license injection script. Your integration GUID in your license file
must match the GUID you insert into the database via the aforementioned InsertLicenseOption injection script, in order for
your GUID to be authorized to integrate with CrossFire on each and every server on which you intend to run your
integration

How to insert an integration GUID license option into C•CURE 9000 database
Your license option is initially added to the database by a script that is provided to you by Software House and is passed
to a tool in the Tyco\CrossFire\Tools folder called InsertLicenseOption.exe. Copy this script to a .bat file in the same
folder. Replace localhost with the appropriate database instance name and then run the .bat file. Here is an example of a
license script:
InsertLicenseOption /U /V /S:"localhost" /N:"Test Control Center" /A:"New Partner"
/G:"F64D393D-D882-4fcf- BDED-FAC8250FB1A8" /C:0 /P:0
/O:"Tester.NextGenConnectedProgram.Common.Objects.Thing"

The parameters for the tool are:


/UpdateOption[+|-] Default value:'-' (short form /U)
/Verbose[+|-] Default value:'-' (short form /V)
/ServerNameInstance:<string> (short form /S)
/Name:<string> (short form /N)
/Author:<string> (short form /A)
/GUID:<string> (short form /G)
/Category:<string> (short form /C)
(Possible Values: 0 = Video 1= Hardware 2 = Software 3 = Other) Product Line:<string> (short
form /P)
(Possible Values: 0 = CCURE 1= Victor 2 = Both)
/ObjectType:<string> (short form /O). An optional flag, which is nearly obsolete, for Video
integrations.

After running this script, restart the CrossFire services for the integration licensing to take effect. After this, open the
Administration Workstation, click Help, and select About. Click the Licensing tab and verify that the newly inserted
license option is listed under Options, at the very bottom of the list. If the option is not presented, review the path of the
database instance and correct it if applicable, and then re-run the script. If the Database insertion was unsuccessful, you
may need to elevate to Administrator permission to run the script. For step by step guidance on this procedure, please
refer to the Step 4 Video in this playlist:
https://www.youtube.com/playlist?list=PLLE4d6pwmNre1OwSkCtB6zMaRERMuZzcM

If your integration is released to customers before your license option is added to C•CURE 9000, it is your responsibility to include
NOTE the provided license option script in your installation program so that your license option is inserted into the C•CURE 9000
database. Otherwise, your customer will not be able to use your integration.
Example: Your integration is released to customers six months after the release of the current C•CURE 9000 version.
Therefore, your license option was not included in the C•CURE 9000 database. This inclusion is necessary only until a
version of C•CURE 9000 is released that has your license option built in.

How to update C•CURE 9000 license to enable the integration license


To check if the integration license is already enabled on a C•CURE 9000 system, there are 2 ways:

Check the license using the License Manager on C•CURE 9000 server:
1. Select one of the following options:
• Click Start > All Programs > Tyco > Licensing.
• On the desktop, click the Licensing icon. You may need to right-click the icon and select Run as Administrator,
depending upon your system security settings.
2. Click the C•CURE 9000 tab (or victor tab if this is a victor system) and look for your integration option in the list of
licensed features.

Check the license using C•CURE 9000 Administration client from any client:
1. Select one of the following options:
• Click Start > All Programs > Tyco > Administration Workstation
• On the desktop, click the Administration Workstation icon.
2. Log in and navigate to Help > About > License.
3. Look for your integration option in the list of licensed features

If your integration license is not enabled on this C•CURE 9000 server, complete the following steps:
■ Please check to make sure you already insert the integration GUID into this C•CURE 9000 system. If the license
option is still not enabled after you already insert the integration GUID, you need to acquire a license from JCI –
Software House team:
■ For development environment, please log a support ticket in the portal to acquire a license from the Connected
Partners Product Management team.
■ For customer production environment, please ask the system integrator or administrator to order an update license for
the system with your integration turn on. Please make sure you already submit your integration to JCI – Software
House Product Management team to get an approval and turn on license on the production system before this license
can be ordered.

Enterprise Integration Considerations


C•CURE 9000 versions 2.0 and later include the Enterprise Architecture option that provides the ability to configure
multiple C•CURE 9000 servers in a single enterprise configuration. An enterprise configuration consists of a Master
Application Server (MAS) and multiple Satellite Application Servers (SAS). The following shows a typical configuration.
Figure 52: Enterprise Architecture Configuration

The Connected Program Kit integrations do not support data synchronization in an Enterprise Architecture system. An
integration can run on a standalone C•CURE 9000 server, or on a SAS within an enterprise. An integration that will run on
a system in an Enterprise Architecture must be built to prevent unintended data synchronization with the Master
Application Server.

The Connected Program Kit is intended to build integrations that can reside on a SAS only. Do not install the Connected Program
NOTE Kit on a MAS, and ensure that your customers do not install your integration on a MAS.

Guidelines for Integrating with Enterprise Architecture

When you are writing a new integration for version 2.0 or later of C•CURE 9000, or updating an integration from a previous
version of C•CURE 9000, you must consider the following guidelines.
■ Fix dangling references [Because starting with 2.0, objectIDs are reused frequently]
• All “single link” references fixed via Foreign key:
— Add foreign key to table which is being referenced.
— Add user-friendly error message.
• More generic references which can be one of several types must be fixed using TargetGUID technique.
■ UI issues with context redirection, executors, etc.:
• If a UI module uses a class derived from BackgroundWorker, it should derive the class from
ContextBackgroundWorker class instead. If a Thread class is used, it is important to propagate the current
context (*both* ISelectedObjectContext and I IRouterSwitchContext ) from the UI thread into the thread. These
classes are rare, used when lengthy operations are needed from UI.
• It is important to know that notification handlers are executed in a separate thread. Use ContextEventHandler
class to create a handler that inherits the current UI context, if the handler calls the server to obtain additional
information (for example if it makes any database call like FindObject).
• If a UI module uses a form and the form is *not* modal, the form should be derived from ContextForm class.
Otherwise, a lot of manual work to manage the context is required.
• Modal forms need to be explicitly disposed of. (Not new for version 2.0 or later, but important).
• Large Majority of the executors behave the same default way in terms of redirecting database access to SAS or
MAS. The algorithm is based on the type (local vs. global) of the selected objects. Usually, local objects redirect
requests to the SAS that owns them and global objects read data from the current connection of the dynamic view.
If it is necessary to change the way an executor reads from the database (to not follow defaults above), it is
possible to implement the interface IContextExecutor in the executor and provide its own context (try to avoid
doing so if possible).
• Code should never create or execute an executor directly. The executor should be always invoked using the
ClientComponent.ExecuteVerb() method.
■ Evaluate the integration's Client UI so that the appropriate objects (such as a list of items, or a button with …) are
shown by selection control:
• By default the selection control filters as follows:
— If the parent object is global, selection control will only display global objects
— If the parent object is local, control should display all global objects and all objects belonging to the app server
of the parent
• You only override this if you don’t want the above behavior. That should be very rare as long as you are not
creating a new global object, or creating a new link table whose parent is a global object. If you do need to override,
one or more of these flags needs to be set when selection control is defined:
— LocalOnly, GlobalOnlyOverride, NoFilter
• A call to ValidateLocalOnlyObject(this) needs to get added to the validation routine of EVERY local-only object.
This will require a small change to every new object created.
• If using Platform code directly, shared AppServerValidationHelper class provides methods for locality and link
validation (Such as ValidateLocalOnlyObject() above).

For more information about integrating with Enterprise Architecture, see Integrating with the Enterprise Architecture
Option.

Enterprise Redirects for FindObject

When operating in an enterprise environment and retrieving objects via the FindObject method in the
IEnhancedDataAccessService interface, the call redirects to the responsible application server for that object. The
responsible application server is determined by the object’s partition ID. Some overrides of FindObject accept the
partition. It is best practice to use these overrides and provide the partition ID. In the case of overrides that do not accept
the partition ID, the local server is queried to attempt to determine the partition ID. If the partition ID cannot be determined,
the call to FindObject may fail. This can occur when querying a SAS for an object owned by another SAS and not
providing the partition ID, or querying the MAS for a newly persisted object owned by a SAS where synchronization back
to the MAS has not yet happened and the partition ID has also not been provided to the call.

Redundancy Extension Considerations


C•CURE 9000 supports the following redundancy options to prevent unintended data loss and/or system downtime.
■ Stratus everRun MX for high availability.
■ Stratus everRun Enterprise (ArcServe) for disaster recovery.

For more information, refer to specific C•CURE 9000 product documentation. For your integration to work when C•CURE
9000 is running a redundancy system, you need to implement the server component as a Windows service.

Core SDK Libraries

SoftwareHouse.CrossFire.Common Namespace

■ SoftwareHouse.CrossFire.Common.Core
The Core library contains classes and methods required to communicate with the Crossfire server. The Core library is
generally used in conjunction with ClientInterfaceLayer library (and other SDK libraries).
■ SoftwareHouse.CrossFire.Common.ClientInterfaceLayer
The ClientInterfaceLayer library contains services and methods for directly communicating with the Crossfire server.
Partners can use the ClientInterfaceLayer library to establish a connection to Crossfire, create objects on the server
(and retrieve a reference to those objects), find objects, update objects, and much more.
The ClientInterfaceLayer also provides the ServiceRequest([…]) method which allows users to directly call services
embedded within Crossfire.
• ServiceRequest([…]) is a powerful way to harness specific Crossfire server functionality. However,
ClientInterfaceLayer features many methods with specific context that wrap the ServiceRequest method. When
applicable, these functions should be used instead.
Example: findObject([…])
ClientInterfaceLayer also provides SDK programmers with the functionality to register callback functions with
Crossfire to allow client applications to be notified (and subsequently perform some action) when specific actions
occur within Crossfire.
■ SoftwareHouse.CrossFire.Common.DataServiceLayer
The DataServiceLayer library contains classes and methods that can be used to create persistent objects, execute
stored procedures, and other perform advanced persistence operations on Crossfire objects. However, most
standard object persistence operations (create, find, persist) are handled by ClientInterfaceLayer (which has an
embedded reference to the DataServiceLayer library).
■ SoftwareHouse.CrossFire.Common.EventServer
The EventServer Library adds functionality to the SDK for dealing with the Crossfire event server mechanism. For
example, SDK programmers can use the EventServer (along with ServiceRequest) to get the Cause List and Cause
Items for a particular Crossfire event).
■ SoftwareHouse.CrossFire.Common.Objects
The objects library contains general-purpose objects that drive the functionality of the Crossfire framework such as
events (XFEvent), maps (Map), Journaling objects, and more.
■ SoftwareHouse.CrossFire.Common.ObjectDefinitions
The ObjectDefinitions library contains the interfaces that define the objects located in the Objects library.
■ SoftwareHouse.CrossFire.Common.Shared
The Shared library contains many general-purpose functions and classes as well as classes used for adding
enterprise (MAS/SAS) support.
■ SoftwareHouse.CrossFire.Common.VideoObjects
The VideoObjects library contains objects needed for video integrations such as VideoCamera objects, VideoServer
objects, and more. These classes can be used directly or derived from in order to implement support for an NVR
previously unsupported by Software House.
■ SoftwareHouse.CrossFire.Common.VideoObjectDefinitions
The VideoObjectDefinitions library contains the interfaces that define the objects located in the VideoObjects library.

SoftwareHouse.NextGen.Common Namespace

■ SoftwareHouse.NextGen.Common.SecurityObjects
The SecurityObjects library contains objects needed for access control integrations (or for integrations that monitor
access control functionality) such as iStarDoors, Doors, Clearance, and Personnel objects.

Debugging a C•CURE 9000 Application


Debugging C•CURE 9000 extensions is made easy through the use of Visual Studio 2019. In order to debug your code,
you must follow the directions below:
1. Set a breakpoint in your code at the area which needs to be examined with the Visual Studio debugger
2. In Visual Studio 2019, click Debug > Attach to Process
3. Select SoftwareHouse.CrossFire.Server.exe and SoftwareHouse.NextGen.Client.AdminWorkstation.exe.
4. Start the debugger and Visual Studio will break when the intended section of code is run.

Installing a Driver Service


To install a ServerComponent DriverService, run the Command Prompt window as an administrator and enter the
following:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\installutil.exe" "C:\Program Files (x86)
\Tyco\CrossFire\ServerComponents\YourDriverServiceFullName.exe

Unsigned Server Component Assemblies can be installed and loaded in the Server Configuration Application, however
this will trigger an Invalid Service error as shown in the following image.
Figure 53: Invalid service error

As a workaround, start up the integration driver service through the Windows Services application. The Server
Configuration Application reflects that your integration driver service is running.
Figure 54: Server Configuration application

Compiling to Run on 64-bit Operating System


C•CURE 9000 clients and Services (drivers) can run on 64-bit Operating Systems as a 32-bit application.

The C•CURE 9000 Server can run on a 64-bit operating System as a 32-bit application, or as a 64-bit application on a 64-
bit Operating System.

Therefore, integrations for C•CURE 9000 need to be compiled using the appropriate C# compiler option.
■ Objects that inherit from DataServiceObjects must be built as AnyCPU (VideoCamera and VideoServer, for example)
to support both 32-bit and 64-bit operation.
■ Services should be built for x86 (32-bit); otherwise they fail to load.

/platform:string
Parameters

string
■ x86 compiles your assembly to be run by the 32-bit, x86-compatible common language runtime.
■ Itanium compiles your assembly to be run by the 64-bit common language runtime on a computer with an Itanium
processor.
■ x64 compiles your assembly to be run by the 64-bit common language runtime on a computer that supports the
AMD64 or EM64T instruction set.
■ anycpu (default) compiles your assembly to run on any platform.

Example:
csc /platform:x86 myIntegration.cs

Upgrading development environment from 2.80 and lower

Connected Partner Program developers

The following instructions are for Connect Programs partners who use the C•CURE 9000 SDK to develop integrations
with C•CURE 9000 v3.00.

Everyone is required to complete the following:


■ Ensure that you have a at least version 16.4.4 of Visual Studio 2019.
■ Ensure that you have .Net Framework 4.8.0 installed. To install, open a .Net Framework project in Visual Studio and
try to change framework version. The system will prompt you to install. Alternatively, click Download .Net
Framework 4.8 Developer Pack from the following website: https://dotnet.microsoft.com/download/dotnet-
framework/net48.
■ Ensure that you have .Net Core 3.1 installed to build and run the iSTAR driver. Install it from the following link:
https://dotnet.microsoft.com/download/dotnet-core/thank-you/sdk-3.1.101-windows-x64-installer

If you already have integration projects on your machine that you intend to reuse after you have installed the latest
C•CURE 9000 version, the following steps may be required.
1. Save all of your integration source, project, and solution files.
2. Wipe out the entire solution area.

Carefully save all of your integration files, but ensure to remove all other files.
NOTE

3. Restore all of the files. Alternatively, you can do the following:


a. Close all solutions including Crossfire SDK, CrossFire Server, Crossfire Client, NextGen Client, and NextGen
server.
b. For each solution, delete the .vs directory found in the same directory as the .sln file.

This will cause you to lose breakpoints and you may have to reset the build version correctly, usually any CPU.
NOTE
4. Optional: Delete the obj directory for all projects, one by one.
5. Rebuild all of your solutions.

You only need to complete these steps once.

Once you have built your integrations, you may need to make your source code to fix specific compile errors. For more
information, see Source code changes.

Source code changes

You need to update an integration project that was built in C•CURE 9000 2.80 or earlier with source code changes, in
order to compile correctly in the latest C•CURE 9000 version environment.
1. Ensure that .Net Framework 4.8.0 and Visual Studio 2019 are installed on your system.
2. Ensure that your build environment works correctly. For more information, see Upgrading development environment
from 2.80 and lower.
3. Open a Connected solution that you would like to build.
4. Update all projects in the solution to .Net framework 4.8.0.
5. In the event that you have some errors and have to add references: The following is a list of common errors and how
to fix them:

Problem: Unable to resolve Impersonation

Solution: Add reference to SoftwareHouse.CrossFire.Common.ClientInterfaceLayer.NetFramework

Problem: Unable to resolve ErrorLogger, or Unable to resolve ISystemTrace or ITraceLevelupdate or


ISystemTraceViewer or ITraceViewer or similar

Solution: To all of the above, add reference to SoftwareHouse.CrossFire.Common.Core.NetFramework

Problem: Unable to resolve ICrossfireUserControl

Solution: Add reference to SoftwareHouse.CrossFire.Common.Interfaces.NetFramework

Problem: Unable to resolve Utilities.SetupHelpProvider

Solution: Add reference to SoftwareHouse.CrossFire.Common.Shared.NetFramework and change call to Utilities_


NetFramework.SetupHelpProvider

Problem: Problems with ObservableDsoCollection and ObserverableCollectionCriteria


Solution: Add reference to SoftwareHouse.CrossFire.Common.DataServiceLayer.NetFramework and use methods in
ObservableDSOCollection_Helper and ObservableCollectionCriteria_Helper.

When adding references, if you are an internal user with access to Crossfire\Output directory, add references to that directory, not
NOTE to NextGen\Bin directory. This does not apply if you are an external user who installs the 9000 and SDK.

Problem: Problems with finding class System.Drawing.Common.DLL (in client code)

Solution: Add reference to System.Drawing.Common.Dll from bin\Client area.

The following is a list of virtually all new .NetFramework DLLs you might have to add:
■ The following DLL comes from UnifiedPlatform\Output. The location matters for internal users only.
• If SoftwareHouse.CrossFire.Common.Shared is in references, you may need to include
SoftwareHouse.CrossFire.Common.Shared.NetFramework.
■ The following DLLs come from Crossfire\Output. The location matters for internal users only.
• If SoftwareHouse.Platform.Crossfire.Common.ClientInterfaceLayer.dll is in references, you may also need to
include SoftwareHouse.Platform.Crossfire.Common.ClientInterfaceLayer.NetFramework.dll
• If SoftwareHouse.Platform.Crossfire.common.core.dll is in references, you may also need to include
SoftwareHouse.Platform.Crossfire.common.core.NetFramework.dll. All drivers may need this reference in at
least one project.
• If SoftwareHouse.Platform.Crossfire.common.DataserviceLayer.dll is in references, you may also need to
include SoftwareHouse.Platform.Crossfire.common.DataserviceLayer.NetFramework.dll
• If ACVS.Enterprise.Common.Services is in references, you may also need to include:
ACVS.Enterprise.Common.Services.NetFramework.dll
■ The following DLLs comes from Nextgen\Bin. The location matters for internal users only.
• If SoftwareHouse.Crossfire.Common.Interfaces is in references, you may also need to include
SoftwareHouse.CrossFire.Common.Interfaces.NetFramework. This is in client code.
• If SoftwareHouse.Platform.Common.Objects.dll is in references, you may also need to include:
SoftwareHouse.Platform.Common.Objects.NetFramework.dll.
■ The following is a reference list of libraries, many from Nuget, that have been updated. If your code is using any of
these libraries and is part of the server or client, you need to update your references to these libraries or you will get
mismatch errors:
• NewtonSoft.Json was updated to 12.0.2. This is found in all three areas: Tyco\CrossFire,
Tyco\CrossFire\ServerComponents, and Tyco\Ccure Client.

Visual Studio Templates


The templates are designed to simplify the task of generating code to interface with Crossfire and hence the C•CURE
9000 system. The templates need to be installed into the development environment in order to function and do not add any
assemblies or code other than that which is created via their execution to the resultant system.
Installing the Templates

There are code generation templates for Visual Studio. These templates generate code which assists in writing plug-ins
for the CrossFire system. There is a template that will write the outline code and project for a client component.

Using the Templates

To use this template, typically you add the template to an existing project that already has one or more classes defined
(but you can add it to a new project and build the classes subsequently).

You can customize the template, for example, to modify the namespace by editing the source files in the zip file. You
unzip the template, edit the template text file, then zip it again to use it with Visual Studio. Refer to the documentation on
MSDN for more information on this technology in Visual Studio.

To add the Template to a new or existing project:


1. In Visual Studio, open an existing project or create a new project.
2. Create a new folder in the project to which you wish to add a client component. Name the folder after the class name
that you want this client component to represent.
3. Right-click the new folder and select Add New Item.
4. Select the CrossFire Client component then change the source file name of the client component to the name you
wish the class to be.
The code will be generated and inserted into this folder and may be edited normally from there.
5. Make sure to target the generated Project to .NET Framework 4.8.

You might also like