///
/// This is a sample C# app for parsing a K2's log file and generating a rough as-run log from it.
/// This generates a log of when assets are loaded, played, stopped, and ejected on each channel.
/// Example output:
///
/// ;--------------------------------------------------------------------------
/// ;Log written: 09/09/09 13:43:49
/// ;
/// ;Date Ch Load Play Stop Eject Asset
/// ;--------------------------------------------------------------------------
/// 08/09/09, C1, 09:48:22, 09:48:32, 09:48:44, 09:49:13, V:/default/Clip
/// 08/09/09, C3, 09:49:21, 09:49:22, 09:49:25, 09:49:25, V:/default/Clip_2
/// 08/09/09, C1, 09:52:04, 09:52:15, 09:53:37, 10:12:28, V:/default/Clip
///
using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Net; // needed for DNS.GetHostName call
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
using GrassValley.Mseries.AppServer;
using GrassValley.Mseries.AppServerLog;
using GrassValley.Mseries.ChanStatus;
using GrassValley.Mseries.ConfigMgr;
using GrassValley.Mseries.Control;
using GrassValley.Mseries.DVCapture;
using GrassValley.Mseries.Editor;
using GrassValley.Mseries.MediaMgr;
using GrassValley.Mseries.Security;
using GrassValley.Mseries.Status;
using GrassValley.Mseries.TransferQueue;
// next lines needed only for running with CS-Script
//css_reference Credentials.dll;
//css_reference ServerUtil.dll;
class K2Sample
{
///
/// Displays the usage.
///
static void displayUsage()
{
Console.WriteLine("Usage:");
Console.WriteLine();
Console.WriteLine("GenerateAsRunLog [-h*ost host]\n");
Console.WriteLine(" -h*ost Host name of K2 Client (default: localhost)");
}
[STAThread]
static void Main(string[] args)
{
// Parse command-line arguments
Arguments CmdArg = new Arguments(args);
// check if the user needs help
if (CmdArg.Exists("?", "help", "usage"))
{
displayUsage();
return;
}
try
{
// setup some variables
AppServerMgrProxy appServerMgrProxy = null;
string appName = Environment.GetCommandLineArgs()[0];
string suiteName = appName + "_" + System.Guid.NewGuid().ToString("N");
string userName = "Administrator";
string password = "adminK2";
string domain = "";
// get hostname from arguments, else default to this machine's name
string host = CmdArg.Value( Dns.GetHostName(), "host", "h").ToUpper();
// create an AppServerMgr proxy object
appServerMgrProxy = new AppServerMgrProxy();
// set the host name of the K2 server
appServerMgrProxy.SetHost(host);
// pass the credentials info
appServerMgrProxy.SetUserCredentials(userName, password, domain, false);
// connect to the AppServerMgr
if ( !appServerMgrProxy.Connect() )
{
appServerMgrProxy = null;
Console.WriteLine("ERROR: Could not connect to AppService on " + host + ".");
return;
}
// create an AppServer
bool newConnection = false;
IAppServer iappServer = appServerMgrProxy.CreateAppServer(suiteName,
appName, out newConnection);
// use ConfigMgr to loop thru all channels. Setup array of ChannelEvents for all channels.
// setup a channel map to convert from channel name to channel ID
Hashtable channelMap = new Hashtable();
// create ConfigMgr
string xmlConfig = "";
IConfigMgr configMgr = iappServer.CreateConfigMgr(appName);
// get config xml document
configMgr.GetConfigXml(out xmlConfig);
// load the xml string into a native XmlDocument
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlConfig);
// setup a namespace manager to parse the xml doc
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(xmlDoc.NameTable);
namespaceManager.AddNamespace("ns", "x-schema:#Schema1");
// get all channels
XmlNodeList channelList = xmlDoc.SelectNodes(("./Config/Channel"), namespaceManager);
if (channelList.Count > 0)
{
IEnumerator channelNodeEnum = channelList.GetEnumerator();
bool bMoreChannels = channelNodeEnum.MoveNext();
// loop thru channels to find their id and channelIndex values
do
{
XmlNode channelNode = (XmlNode)channelNodeEnum.Current;
string channelName = channelNode.Attributes.GetNamedItem("id").Value;
Int32 channelIndex = Int32.Parse(channelNode.Attributes.GetNamedItem("channelIndex").Value);
channelMap.Add(channelName, channelIndex);
bMoreChannels = channelNodeEnum.MoveNext();
} while (bMoreChannels);
}
//cleanup
configMgr.Dispose();
// setup a temporary event array for all channels.
ChannelEvent[] channelEvent = new ChannelEvent[4];
// create an array list to store all events
ArrayList asRunList = new ArrayList();
// get a log object from the AppServer
IAppServerLog3 log = (IAppServerLog3) iappServer.CreateLog(appName);
// open the K2's log file
log.Open("C:\\logs\\mlog.mmf");
// set the format to get strings out rather than binary data
log.ReadFormat = "string";
int maxMsgCount = log.MaxMsgCount; // max # of messages
// get the last messages from the bottom of the log
int actualCount = 0; // var used to tell number objects returned
object[] last = (object[]) log.ReadLastMsg(99999, ref actualCount);
foreach (object o in last)
{
string s = (string) o;
// check for loading, ejecting, playing, or stopping strings
if ( -1 != s.IndexOf(host) &&
( -1 != s.IndexOf("Loading") ||
-1 != s.IndexOf("Ejecting") ||
-1 != s.IndexOf("portState=Play speed=1.00") ||
-1 != s.IndexOf("stop")
)
)
{
// get channel name from string
string channelName = "";
// check for context message
int channelIndex = s.IndexOf(":Ctx");
// or if not found, check for a loading message
if ( -1 == channelIndex )
channelIndex = s.IndexOf(": Loading");
// if index found, get the channel name substring
if ( -1 != channelIndex )
{
channelIndex -= 2;
channelName = s.Substring(channelIndex, 2);
}
// skip thru loop if channel name isn't found
if ( String.IsNullOrEmpty(channelName) )
continue;
// get the real channel index from the channel map
channelIndex = (int)channelMap[channelName];
// if a channel event doesn't exist yet, create one
if ( null == channelEvent[channelIndex] )
channelEvent[channelIndex] = new ChannelEvent(channelName);
// get the datetime of this event
int idx = s.IndexOf(host);
string logtime = s.Substring(4, idx-4);
DateTime logdatetime = DateTime.Parse(logtime);
// if load time found
if ( -1 != s.IndexOf("Loading") )
{
channelEvent[channelIndex].LoadTime = logdatetime;
int assetIdx = s.IndexOf("edl/cmf//");
string asset = s.Substring(assetIdx, s.Length - assetIdx - 1);
assetIdx = asset.IndexOf(":");
channelEvent[channelIndex].Asset = asset.Substring(assetIdx - 1, asset.Length - assetIdx + 1);
}
// if ejecting time found
else if ( -1 != s.IndexOf("Ejecting") )
{
// set the event's eject time
channelEvent[channelIndex].EjectTime = logdatetime;
// if event's play time was set, but its stop time was NOT set,
// set the stop time the same as the eject time.
if (
(DateTime.MinValue != channelEvent[channelIndex].PlayTime) &&
(DateTime.MinValue == channelEvent[channelIndex].StopTime)
)
channelEvent[channelIndex].StopTime = logdatetime;
// add event to as-run list, then clear the event list
asRunList.Add(channelEvent[channelIndex]);
channelEvent[channelIndex] = null;
}
// if play time found & not previously set
else if ( -1 != s.IndexOf("portState=Play speed=1.00") &&
DateTime.MinValue == channelEvent[channelIndex].PlayTime
)
channelEvent[channelIndex].PlayTime = logdatetime;
// if stop time found
else if ( -1 != s.IndexOf("stop") )
channelEvent[channelIndex].StopTime = logdatetime;
}
}
// close log and cleanup appserver
log.Close();
iappServer.CloseConnection();
// print all as-run events
// print header
Console.WriteLine(";--------------------------------------------------------------------------");
Console.WriteLine(";Log written: {0}", DateTime.Now.ToString("dd/MM/yy HH:mm:ss") );
Console.WriteLine(";");
Console.WriteLine(";Date Ch Load Play Stop Eject Asset");
Console.WriteLine(";--------------------------------------------------------------------------");
// print events
for (int i = 0; i < asRunList.Count; i++)
{
((ChannelEvent)asRunList[i]).print();
}
}
catch (SystemException se)
{
Console.WriteLine(se);
}
}
}
public class ChannelEvent
{
string _channel = null;
string _asset = null;
DateTime _loadTime = DateTime.MinValue;
DateTime _playTime = DateTime.MinValue;
DateTime _stopTime = DateTime.MinValue;
DateTime _ejectTime = DateTime.MinValue;
public void print()
{
// only print events that played
if (DateTime.MinValue != _playTime)
{
Console.Write("{0:dd/MM/yy}, {1}, ", _playTime, _channel);
if (DateTime.MinValue != _loadTime)
Console.Write("{0:HH:mm:ss}, ", _loadTime);
else
Console.Write("--------, ");
if (DateTime.MinValue != _playTime)
Console.Write("{0:HH:mm:ss}, ", _playTime);
else
Console.Write("--------, ");
if (DateTime.MinValue != _stopTime)
Console.Write("{0:HH:mm:ss}, ", _stopTime);
else
Console.Write("--------, ");
if (DateTime.MinValue != _ejectTime)
Console.Write("{0:HH:mm:ss}, ", _ejectTime);
else
Console.Write("--------, ");
Console.WriteLine(_asset);
}
}
public ChannelEvent(string channel)
{
_channel = channel;
}
public string Channel
{
get { return _channel; }
set { _channel = value; }
}
public string Asset
{
get { return _asset; }
set { _asset = value; }
}
public DateTime LoadTime
{
get { return _loadTime; }
set { _loadTime = value; }
}
public DateTime PlayTime
{
get { return _playTime; }
set { _playTime = value; }
}
public DateTime StopTime
{
get { return _stopTime; }
set { _stopTime = value; }
}
public DateTime EjectTime
{
get { return _ejectTime; }
set { _ejectTime = value; }
}
}
///
/// Class Arguments is used to parse command-line arguments and stdin arguments
///
public class Arguments
{
// structures used by calls to read StdIn.
internal struct COORD
{
internal short X;
internal short Y;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct KeyEventRecord
{
internal bool keyDown;
internal ushort repeatCount;
internal ushort virtualKeyCode;
internal ushort virtualScanCode;
internal char unicodeChar;
internal uint controlKeyState;
}
internal struct MouseEventRecord
{
internal COORD mousePosition;
internal uint bottonState;
internal uint controlKeyState;
internal uint eventFlags;
}
internal struct WindowBufSize
{
internal COORD size;
}
internal struct MenuEventREcord
{
internal uint cmdId;
}
internal struct FocusEventRecord
{
internal bool setFoucus;
}
internal struct InputRecord
{
internal EventTypes eventType;
internal KeyEventRecord keyEvent;
internal MouseEventRecord mouseEvent;
internal WindowBufSize bufSize;
internal MenuEventREcord menuEvent;
internal FocusEventRecord focusEvent;
}
internal enum EventTypes : ushort
{
KeyEvent = 0x01,
MouseEvent = 0x02,
WindowBufSizeEvent = 0x04,
MenuEvent = 0x08,
FocusEvent = 0x10
}
internal class Constants
{
internal const int StdInput = -10;
internal const int StdOutput = -11;
internal const int StdError = -12;
internal static readonly IntPtr InvalidHandle = new IntPtr(-1);
}
///
/// Gets the STD handle.
///
/// The STD handle.
/// IntPtr handle
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetStdHandle(int stdHandle);
///
/// Peeks the console input.
///
/// The console handle.
/// The buffer.
/// Length of the buf.
/// The num of events.
/// bool
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool PeekConsoleInput(IntPtr consoleHandle,
[Out] InputRecord[] buffer, uint bufLength, out uint numOfEvents);
// Variables
private StringDictionary Parameters;
private ArrayList defaultArguments;
///
/// Initializes a new instance of the class.
///
/// The args.
public Arguments(string[] Args)
{
// setup dictionary and list
Parameters = new StringDictionary();
defaultArguments = new ArrayList();
ArrayList tempArgs = new ArrayList(Args);
// get stdin passed in values
IntPtr stdin = GetStdHandle(Constants.StdInput);
InputRecord[] records = new InputRecord[1];
uint eventsRead = 0;
// if arguments passed in standard input, then add them to the array
if (!PeekConsoleInput(stdin, records, (uint)records.Length, out eventsRead))
{
int argPositionCounter = 0;
string currentLine = Console.In.ReadLine();
while (currentLine != null)
{
string[] split = currentLine.Split(new Char[] { ' ', ',' });
foreach (string s in split)
{
if (s.Trim() != "")
{
tempArgs.Insert(argPositionCounter, s);
argPositionCounter++;
}
}
currentLine = Console.In.ReadLine();
}
}
// convert to string[], clear out temporary arraylist
Args = (string[])tempArgs.ToArray(typeof(string));
tempArgs.Clear();
Regex Spliter = new Regex(@"^-{1,2}|^/|=",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
Regex Remover = new Regex(@"^['""]?(.*?)['""]?$",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
string Parameter = null;
string[] Parts;
// Valid parameters forms:
// {-,/,--}param{ ,=,:}((",')value(",'))
// Examples:
// -param1 value1 --param2 /param3:"Test-:-work"
// /param4=happy -param5 '--=nice=--'
foreach (string Txt in Args)
{
// Look for new parameters (-,/ or --) and a
// possible enclosed value (=,:)
Parts = Spliter.Split(Txt, 3);
switch (Parts.Length)
{
// Found a value (for the last parameter
// found (space separator))
case 1:
if (Parameter != null)
{
if (!Parameters.ContainsKey(Parameter))
{
Parts[0] =
Remover.Replace(Parts[0], "$1");
Parameters.Add(Parameter, Parts[0]);
}
Parameter = null;
}
// no parameter, so add it to the default arguments
else
defaultArguments.Add(Txt);
break;
// Found just a parameter
case 2:
// The last parameter is still waiting.
// With no value, set it to true.
if (Parameter != null)
{
if (!Parameters.ContainsKey(Parameter))
{
Parameters.Add(Parameter, "true");
}
}
Parameter = Parts[1];
break;
// Parameter with enclosed value
case 3:
// The last parameter is still waiting.
// With no value, set it to true.
if (Parameter != null)
{
if (!Parameters.ContainsKey(Parameter))
{
Parameters.Add(Parameter, "true");
}
}
Parameter = Parts[1];
// Remove possible enclosing characters (",')
if (!Parameters.ContainsKey(Parameter))
{
Parts[2] = Remover.Replace(Parts[2], "$1");
Parameters.Add(Parameter, Parts[2]);
}
Parameter = null;
break;
}
}
// In case a parameter is still waiting
if (Parameter != null)
{
if (!Parameters.ContainsKey(Parameter))
Parameters.Add(Parameter, "true");
}
}
// Retrieve a parameter value if it exists
// (overriding C# indexer property)
public string this[string Param]
{
get
{
return (Parameters[Param]);
}
}
// checks if one or more parameter values exist
// example usage:
// if( Arguments.Exists("?", "help", "usage") )
// displayUsage();
public bool Exists(params string[] Params)
{
foreach (string param in Params)
{
if (null != Parameters[param])
return true;
}
return false;
}
// returns a parameter value if it exists; else returns defaultValue
// example usage: string host = Arguments.Value("localhost", "host", "h");
// if param "host" or "h" is passed in, use that value. If not, use the string "localhost"
public string Value(string defaultValue, params string[] Params)
{
foreach (string param in Params)
{
if (null != Parameters[param])
return (Parameters[param]);
}
return defaultValue;
}
public string[] DefaultArguments
{
get
{
string[] defaultArgs = (string[])defaultArguments.ToArray(typeof(string));
return defaultArgs;
}
}
}