/// /// 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; } } }