From 134f86e8d5c414409631b25b8c6f0ee45fbd8631 Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Thu, 3 Nov 2016 21:44:39 +1000 Subject: Initial update to OpenSim 0.8.2.1 source code. --- .../UserStatistics/ActiveConnectionsAJAX.cs | 308 +++++ .../UserStatistics/Clients_report.cs | 329 ++++++ .../UserStatistics/Default_Report.cs | 277 +++++ .../OptionalModules/UserStatistics/HTMLUtil.cs | 263 +++++ .../OptionalModules/UserStatistics/IStatsReport.cs | 39 + .../OptionalModules/UserStatistics/LogLinesAJAX.cs | 159 +++ .../UserStatistics/Prototype_distributor.cs | 80 ++ .../UserStatistics/Sessions_Report.cs | 288 +++++ .../OptionalModules/UserStatistics/SimStatsAJAX.cs | 276 +++++ .../UserStatistics/Updater_distributor.cs | 70 ++ .../UserStatistics/WebStatsModule.cs | 1203 ++++++++++++++++++++ 11 files changed, 3292 insertions(+) create mode 100644 OpenSim/Region/OptionalModules/UserStatistics/ActiveConnectionsAJAX.cs create mode 100644 OpenSim/Region/OptionalModules/UserStatistics/Clients_report.cs create mode 100644 OpenSim/Region/OptionalModules/UserStatistics/Default_Report.cs create mode 100644 OpenSim/Region/OptionalModules/UserStatistics/HTMLUtil.cs create mode 100644 OpenSim/Region/OptionalModules/UserStatistics/IStatsReport.cs create mode 100644 OpenSim/Region/OptionalModules/UserStatistics/LogLinesAJAX.cs create mode 100644 OpenSim/Region/OptionalModules/UserStatistics/Prototype_distributor.cs create mode 100644 OpenSim/Region/OptionalModules/UserStatistics/Sessions_Report.cs create mode 100644 OpenSim/Region/OptionalModules/UserStatistics/SimStatsAJAX.cs create mode 100644 OpenSim/Region/OptionalModules/UserStatistics/Updater_distributor.cs create mode 100644 OpenSim/Region/OptionalModules/UserStatistics/WebStatsModule.cs (limited to 'OpenSim/Region/OptionalModules/UserStatistics') diff --git a/OpenSim/Region/OptionalModules/UserStatistics/ActiveConnectionsAJAX.cs b/OpenSim/Region/OptionalModules/UserStatistics/ActiveConnectionsAJAX.cs new file mode 100644 index 0000000..6a1112c --- /dev/null +++ b/OpenSim/Region/OptionalModules/UserStatistics/ActiveConnectionsAJAX.cs @@ -0,0 +1,308 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using Mono.Data.SqliteClient; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Framework.Monitoring; + +namespace OpenSim.Region.UserStatistics +{ + public class ActiveConnectionsAJAX : IStatsController + { + private Vector3 DefaultNeighborPosition = new Vector3(((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), 70); + + #region IStatsController Members + + public string ReportName + { + get { return ""; } + } + + public Hashtable ProcessModel(Hashtable pParams) + { + List m_scene = (List)pParams["Scenes"]; + + Hashtable nh = new Hashtable(); + nh.Add("hdata", m_scene); + + return nh; + } + + public string RenderView(Hashtable pModelResult) + { + List all_scenes = (List) pModelResult["hdata"]; + + StringBuilder output = new StringBuilder(); + HTMLUtil.OL_O(ref output, ""); + foreach (Scene scene in all_scenes) + { + HTMLUtil.LI_O(ref output, String.Empty); + output.Append(scene.RegionInfo.RegionName); + HTMLUtil.OL_O(ref output, String.Empty); + scene.ForEachScenePresence(delegate(ScenePresence av) + { + Dictionary queues = new Dictionary(); + if (av.ControllingClient is IStatsCollector) + { + IStatsCollector isClient = (IStatsCollector)av.ControllingClient; + queues = decodeQueueReport(isClient.Report()); + } + HTMLUtil.LI_O(ref output, String.Empty); + output.Append(av.Name); + output.Append("      "); + output.Append((av.IsChildAgent ? "Child" : "Root")); + if (av.AbsolutePosition == DefaultNeighborPosition) + { + output.Append("
Position: ?"); + } + else + { + output.Append(string.Format("
Position: <{0},{1},{2}>", (int)av.AbsolutePosition.X, + (int)av.AbsolutePosition.Y, + (int)av.AbsolutePosition.Z)); + } + Dictionary throttles = DecodeClientThrottles(av.ControllingClient.GetThrottlesPacked(1)); + + HTMLUtil.UL_O(ref output, String.Empty); + + foreach (string throttlename in throttles.Keys) + { + HTMLUtil.LI_O(ref output, String.Empty); + output.Append(throttlename); + output.Append(":"); + output.Append(throttles[throttlename].ToString()); + if (queues.ContainsKey(throttlename)) + { + output.Append("/"); + output.Append(queues[throttlename]); + } + HTMLUtil.LI_C(ref output); + } + if (queues.ContainsKey("Incoming") && queues.ContainsKey("Outgoing")) + { + HTMLUtil.LI_O(ref output, "red"); + output.Append("SEND:"); + output.Append(queues["Outgoing"]); + output.Append("/"); + output.Append(queues["Incoming"]); + HTMLUtil.LI_C(ref output); + } + + HTMLUtil.UL_C(ref output); + HTMLUtil.LI_C(ref output); + }); + HTMLUtil.OL_C(ref output); + } + HTMLUtil.OL_C(ref output); + return output.ToString(); + } + + /// + /// Convert active connections information to JSON string. Returns a structure: + ///
+        /// {"regionName": {
+        ///     "presenceName": {
+        ///         "name": "presenceName",
+        ///         "position": "",
+        ///         "isRoot": "false",
+        ///         "throttle": {
+        ///         },
+        ///         "queue": {
+        ///         }
+        ///     },
+        ///     ... // multiple presences in the scene
+        ///   },
+        ///   ...   // multiple regions in the sim
+        /// }
+        ///
+        /// 
+ ///
+ /// + /// + public string RenderJson(Hashtable pModelResult) + { + List all_scenes = (List) pModelResult["hdata"]; + + OSDMap regionInfo = new OSDMap(); + foreach (Scene scene in all_scenes) + { + OSDMap sceneInfo = new OpenMetaverse.StructuredData.OSDMap(); + List avatarInScene = scene.GetScenePresences(); + foreach (ScenePresence av in avatarInScene) + { + OSDMap presenceInfo = new OSDMap(); + presenceInfo.Add("Name", new OSDString(av.Name)); + + Dictionary queues = new Dictionary(); + if (av.ControllingClient is IStatsCollector) + { + IStatsCollector isClient = (IStatsCollector) av.ControllingClient; + queues = decodeQueueReport(isClient.Report()); + } + OSDMap queueInfo = new OpenMetaverse.StructuredData.OSDMap(); + foreach (KeyValuePair kvp in queues) { + queueInfo.Add(kvp.Key, new OSDString(kvp.Value)); + } + sceneInfo.Add("queues", queueInfo); + + if (av.IsChildAgent) + presenceInfo.Add("isRoot", new OSDString("false")); + else + presenceInfo.Add("isRoot", new OSDString("true")); + + if (av.AbsolutePosition == DefaultNeighborPosition) + { + presenceInfo.Add("position", new OSDString("<0, 0, 0>")); + } + else + { + presenceInfo.Add("position", new OSDString(string.Format("<{0},{1},{2}>", + (int)av.AbsolutePosition.X, + (int) av.AbsolutePosition.Y, + (int) av.AbsolutePosition.Z)) ); + } + + Dictionary throttles = DecodeClientThrottles(av.ControllingClient.GetThrottlesPacked(1)); + OSDMap throttleInfo = new OpenMetaverse.StructuredData.OSDMap(); + foreach (string throttlename in throttles.Keys) + { + throttleInfo.Add(throttlename, new OSDString(throttles[throttlename].ToString())); + } + presenceInfo.Add("throttle", throttleInfo); + + sceneInfo.Add(av.Name, presenceInfo); + } + regionInfo.Add(scene.RegionInfo.RegionName, sceneInfo); + } + return regionInfo.ToString(); + } + + public Dictionary DecodeClientThrottles(byte[] throttle) + { + Dictionary returndict = new Dictionary(); + // From mantis http://opensimulator.org/mantis/view.php?id=1374 + // it appears that sometimes we are receiving empty throttle byte arrays. + // TODO: Investigate this behaviour + if (throttle.Length == 0) + { + return new Dictionary(); + } + + int tResend = -1; + int tLand = -1; + int tWind = -1; + int tCloud = -1; + int tTask = -1; + int tTexture = -1; + int tAsset = -1; + int tall = -1; + const int singlefloat = 4; + + //Agent Throttle Block contains 7 single floatingpoint values. + int j = 0; + + // Some Systems may be big endian... + // it might be smart to do this check more often... + if (!BitConverter.IsLittleEndian) + for (int i = 0; i < 7; i++) + Array.Reverse(throttle, j + i * singlefloat, singlefloat); + + // values gotten from OpenMetaverse.org/wiki/Throttle. Thanks MW_ + // bytes + // Convert to integer, since.. the full fp space isn't used. + tResend = (int)BitConverter.ToSingle(throttle, j); + returndict.Add("Resend", tResend); + j += singlefloat; + tLand = (int)BitConverter.ToSingle(throttle, j); + returndict.Add("Land", tLand); + j += singlefloat; + tWind = (int)BitConverter.ToSingle(throttle, j); + returndict.Add("Wind", tWind); + j += singlefloat; + tCloud = (int)BitConverter.ToSingle(throttle, j); + returndict.Add("Cloud", tCloud); + j += singlefloat; + tTask = (int)BitConverter.ToSingle(throttle, j); + returndict.Add("Task", tTask); + j += singlefloat; + tTexture = (int)BitConverter.ToSingle(throttle, j); + returndict.Add("Texture", tTexture); + j += singlefloat; + tAsset = (int)BitConverter.ToSingle(throttle, j); + returndict.Add("Asset", tAsset); + + tall = tResend + tLand + tWind + tCloud + tTask + tTexture + tAsset; + returndict.Add("All", tall); + + return returndict; + } + public Dictionary decodeQueueReport(string rep) + { + Dictionary returndic = new Dictionary(); + if (rep.Length == 79) + { + int pos = 1; + returndic.Add("All", rep.Substring((6 * pos), 8)); pos++; + returndic.Add("Incoming", rep.Substring((7 * pos), 8)); pos++; + returndic.Add("Outgoing", rep.Substring((7 * pos) , 8)); pos++; + returndic.Add("Resend", rep.Substring((7 * pos) , 8)); pos++; + returndic.Add("Land", rep.Substring((7 * pos) , 8)); pos++; + returndic.Add("Wind", rep.Substring((7 * pos) , 8)); pos++; + returndic.Add("Cloud", rep.Substring((7 * pos) , 8)); pos++; + returndic.Add("Task", rep.Substring((7 * pos) , 8)); pos++; + returndic.Add("Texture", rep.Substring((7 * pos), 8)); pos++; + returndic.Add("Asset", rep.Substring((7 * pos), 8)); + /* + * return string.Format("{0,7} {1,7} {2,7} {3,7} {4,7} {5,7} {6,7} {7,7} {8,7} {9,7}", + SendQueue.Count(), + IncomingPacketQueue.Count, + OutgoingPacketQueue.Count, + ResendOutgoingPacketQueue.Count, + LandOutgoingPacketQueue.Count, + WindOutgoingPacketQueue.Count, + CloudOutgoingPacketQueue.Count, + TaskOutgoingPacketQueue.Count, + TextureOutgoingPacketQueue.Count, + AssetOutgoingPacketQueue.Count); + */ + } + + + + return returndic; + } + #endregion + } +} diff --git a/OpenSim/Region/OptionalModules/UserStatistics/Clients_report.cs b/OpenSim/Region/OptionalModules/UserStatistics/Clients_report.cs new file mode 100644 index 0000000..4a6f7be --- /dev/null +++ b/OpenSim/Region/OptionalModules/UserStatistics/Clients_report.cs @@ -0,0 +1,329 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using Mono.Data.SqliteClient; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.UserStatistics +{ + public class Clients_report : IStatsController + { + #region IStatsController Members + + public string ReportName + { + get { return "Client"; } + } + + /// + /// Return summar information in the form: + ///
+        /// {"totalUsers": "34",
+        ///  "totalSessions": "233",
+        ///  ...
+        /// }
+        /// 
+ ///
+ /// + /// + public string RenderJson(Hashtable pModelResult) { + stats_default_page_values values = (stats_default_page_values) pModelResult["hdata"]; + + OSDMap summaryInfo = new OpenMetaverse.StructuredData.OSDMap(); + summaryInfo.Add("totalUsers", new OSDString(values.total_num_users.ToString())); + summaryInfo.Add("totalSessions", new OSDString(values.total_num_sessions.ToString())); + summaryInfo.Add("averageClientFPS", new OSDString(values.avg_client_fps.ToString())); + summaryInfo.Add("averageClientMem", new OSDString(values.avg_client_mem_use.ToString())); + summaryInfo.Add("averageSimFPS", new OSDString(values.avg_sim_fps.ToString())); + summaryInfo.Add("averagePingTime", new OSDString(values.avg_ping.ToString())); + summaryInfo.Add("totalKBOut", new OSDString(values.total_kb_out.ToString())); + summaryInfo.Add("totalKBIn", new OSDString(values.total_kb_in.ToString())); + return summaryInfo.ToString(); + } + + public Hashtable ProcessModel(Hashtable pParams) + { + SqliteConnection dbConn = (SqliteConnection)pParams["DatabaseConnection"]; + + + List clidata = new List(); + List cliRegData = new List(); + Hashtable regionTotals = new Hashtable(); + + Hashtable modeldata = new Hashtable(); + modeldata.Add("Scenes", pParams["Scenes"]); + modeldata.Add("Reports", pParams["Reports"]); + int totalclients = 0; + int totalregions = 0; + + lock (dbConn) + { + string sql = "select count(distinct region_id) as regcnt from stats_session_data"; + + SqliteCommand cmd = new SqliteCommand(sql, dbConn); + SqliteDataReader sdr = cmd.ExecuteReader(); + if (sdr.HasRows) + { + sdr.Read(); + totalregions = Convert.ToInt32(sdr["regcnt"]); + } + + sdr.Close(); + sdr.Dispose(); + + sql = + "select client_version, count(*) as cnt, avg(avg_sim_fps) as simfps from stats_session_data group by client_version order by count(*) desc LIMIT 10;"; + + cmd = new SqliteCommand(sql, dbConn); + sdr = cmd.ExecuteReader(); + if (sdr.HasRows) + { + while (sdr.Read()) + { + ClientVersionData udata = new ClientVersionData(); + udata.version = sdr["client_version"].ToString(); + udata.count = Convert.ToInt32(sdr["cnt"]); + udata.fps = Convert.ToSingle(sdr["simfps"]); + clidata.Add(udata); + totalclients += udata.count; + + } + } + sdr.Close(); + sdr.Dispose(); + + if (totalregions > 1) + { + sql = + "select region_id, client_version, count(*) as cnt, avg(avg_sim_fps) as simfps from stats_session_data group by region_id, client_version order by region_id, count(*) desc;"; + cmd = new SqliteCommand(sql, dbConn); + + sdr = cmd.ExecuteReader(); + + if (sdr.HasRows) + { + while (sdr.Read()) + { + ClientVersionData udata = new ClientVersionData(); + udata.version = sdr["client_version"].ToString(); + udata.count = Convert.ToInt32(sdr["cnt"]); + udata.fps = Convert.ToSingle(sdr["simfps"]); + udata.region_id = UUID.Parse(sdr["region_id"].ToString()); + cliRegData.Add(udata); + } + } + sdr.Close(); + sdr.Dispose(); + + + } + + } + + foreach (ClientVersionData cvd in cliRegData) + { + + if (regionTotals.ContainsKey(cvd.region_id)) + { + int regiontotal = (int)regionTotals[cvd.region_id]; + regiontotal += cvd.count; + regionTotals[cvd.region_id] = regiontotal; + } + else + { + regionTotals.Add(cvd.region_id, cvd.count); + } + + + + } + + modeldata["ClientData"] = clidata; + modeldata["ClientRegionData"] = cliRegData; + modeldata["RegionTotals"] = regionTotals; + modeldata["Total"] = totalclients; + + return modeldata; + } + + public string RenderView(Hashtable pModelResult) + { + List clidata = (List) pModelResult["ClientData"]; + int totalclients = (int)pModelResult["Total"]; + Hashtable regionTotals = (Hashtable) pModelResult["RegionTotals"]; + List cliRegData = (List) pModelResult["ClientRegionData"]; + List m_scenes = (List)pModelResult["Scenes"]; + Dictionary reports = (Dictionary)pModelResult["Reports"]; + + const string STYLESHEET = + @" + +"; + + StringBuilder output = new StringBuilder(); + HTMLUtil.HtmlHeaders_O(ref output); + output.Append(STYLESHEET); + HTMLUtil.HtmlHeaders_C(ref output); + + HTMLUtil.AddReportLinks(ref output, reports, ""); + + HTMLUtil.TABLE_O(ref output, "defaultr"); + HTMLUtil.TR_O(ref output, ""); + HTMLUtil.TD_O(ref output, "header"); + output.Append("ClientVersion"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "header"); + output.Append("Count/%"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "header"); + output.Append("SimFPS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + + foreach (ClientVersionData cvd in clidata) + { + HTMLUtil.TR_O(ref output, ""); + HTMLUtil.TD_O(ref output, "content"); + string linkhref = "sessions.report?VersionString=" + cvd.version; + HTMLUtil.A(ref output, cvd.version, linkhref, ""); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "content"); + output.Append(cvd.count); + output.Append("/"); + if (totalclients > 0) + output.Append((((float)cvd.count / (float)totalclients)*100).ToString()); + else + output.Append(0); + + output.Append("%"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "content"); + output.Append(cvd.fps); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + } + HTMLUtil.TABLE_C(ref output); + + if (cliRegData.Count > 0) + { + HTMLUtil.TABLE_O(ref output, "defaultr"); + HTMLUtil.TR_O(ref output, ""); + HTMLUtil.TD_O(ref output, "header"); + output.Append("Region"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "header"); + output.Append("ClientVersion"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "header"); + output.Append("Count/%"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "header"); + output.Append("SimFPS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + + foreach (ClientVersionData cvd in cliRegData) + { + HTMLUtil.TR_O(ref output, ""); + HTMLUtil.TD_O(ref output, "content"); + output.Append(regionNamefromUUID(m_scenes, cvd.region_id)); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "content"); + output.Append(cvd.version); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "content"); + output.Append(cvd.count); + output.Append("/"); + if ((int)regionTotals[cvd.region_id] > 0) + output.Append((((float)cvd.count / (float)((int)regionTotals[cvd.region_id])) * 100).ToString()); + else + output.Append(0); + + output.Append("%"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "content"); + output.Append(cvd.fps); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + } + HTMLUtil.TABLE_C(ref output); + + } + + output.Append(""); + output.Append(""); + return output.ToString(); + } + public string regionNamefromUUID(List scenes, UUID region_id) + { + string returnstring = string.Empty; + foreach (Scene sn in scenes) + { + if (region_id == sn.RegionInfo.originRegionID) + { + returnstring = sn.RegionInfo.RegionName; + break; + } + } + + if (returnstring.Length == 0) + { + returnstring = region_id.ToString(); + } + + return returnstring; + } + + #endregion + } + + public struct ClientVersionData + { + public UUID region_id; + public string version; + public int count; + public float fps; + } +} diff --git a/OpenSim/Region/OptionalModules/UserStatistics/Default_Report.cs b/OpenSim/Region/OptionalModules/UserStatistics/Default_Report.cs new file mode 100644 index 0000000..fabe3d4 --- /dev/null +++ b/OpenSim/Region/OptionalModules/UserStatistics/Default_Report.cs @@ -0,0 +1,277 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using Mono.Data.SqliteClient; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Framework.Monitoring; + + +namespace OpenSim.Region.UserStatistics +{ + public class Default_Report : IStatsController + { + + public string ReportName + { + get { return "Home"; } + } + + #region IStatsController Members + + public Hashtable ProcessModel(Hashtable pParams) + { + SqliteConnection conn = (SqliteConnection)pParams["DatabaseConnection"]; + List m_scene = (List)pParams["Scenes"]; + + stats_default_page_values mData = rep_DefaultReport_data(conn, m_scene); + mData.sim_stat_data = (Dictionary)pParams["SimStats"]; + mData.stats_reports = (Dictionary) pParams["Reports"]; + + Hashtable nh = new Hashtable(); + nh.Add("hdata", mData); + nh.Add("Reports", pParams["Reports"]); + + return nh; + } + + public string RenderView(Hashtable pModelResult) + { + stats_default_page_values mData = (stats_default_page_values) pModelResult["hdata"]; + return rep_Default_report_view(mData); + } + + #endregion + + public string rep_Default_report_view(stats_default_page_values values) + { + + + StringBuilder output = new StringBuilder(); + + + + const string TableClass = "defaultr"; + const string TRClass = "defaultr"; + const string TDHeaderClass = "header"; + const string TDDataClass = "content"; + //const string TDDataClassRight = "contentright"; + const string TDDataClassCenter = "contentcenter"; + + const string STYLESHEET = + @" + +"; + HTMLUtil.HtmlHeaders_O(ref output); + + HTMLUtil.InsertProtoTypeAJAX(ref output); + string[] ajaxUpdaterDivs = new string[3]; + int[] ajaxUpdaterSeconds = new int[3]; + string[] ajaxUpdaterReportFragments = new string[3]; + + ajaxUpdaterDivs[0] = "activeconnections"; + ajaxUpdaterSeconds[0] = 10; + ajaxUpdaterReportFragments[0] = "activeconnectionsajax.html"; + + ajaxUpdaterDivs[1] = "activesimstats"; + ajaxUpdaterSeconds[1] = 20; + ajaxUpdaterReportFragments[1] = "simstatsajax.html"; + + ajaxUpdaterDivs[2] = "activelog"; + ajaxUpdaterSeconds[2] = 5; + ajaxUpdaterReportFragments[2] = "activelogajax.html"; + + HTMLUtil.InsertPeriodicUpdaters(ref output, ajaxUpdaterDivs, ajaxUpdaterSeconds, ajaxUpdaterReportFragments); + + output.Append(STYLESHEET); + HTMLUtil.HtmlHeaders_C(ref output); + HTMLUtil.AddReportLinks(ref output, values.stats_reports, ""); + HTMLUtil.TABLE_O(ref output, TableClass); + HTMLUtil.TR_O(ref output, TRClass); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("# Users Total"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("# Sessions Total"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("Avg Client FPS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("Avg Client Mem Use"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("Avg Sim FPS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("Avg Ping"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("KB Out Total"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("KB In Total"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + HTMLUtil.TR_O(ref output, TRClass); + HTMLUtil.TD_O(ref output, TDDataClass); + output.Append(values.total_num_users); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClass); + output.Append(values.total_num_sessions); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(values.avg_client_fps); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(values.avg_client_mem_use); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(values.avg_sim_fps); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(values.avg_ping); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(values.total_kb_out); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(values.total_kb_in); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + HTMLUtil.TABLE_C(ref output); + + HTMLUtil.HR(ref output, ""); + HTMLUtil.TABLE_O(ref output, ""); + HTMLUtil.TR_O(ref output, ""); + HTMLUtil.TD_O(ref output, "align_top"); + output.Append("
Active Connections loading...
"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "align_top"); + output.Append("
SimStats loading...
"); + output.Append("
ActiveLog loading...
"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + HTMLUtil.TABLE_C(ref output); + output.Append(""); + // TODO: FIXME: template + return output.ToString(); + } + + + + public stats_default_page_values rep_DefaultReport_data(SqliteConnection db, List m_scene) + { + stats_default_page_values returnstruct = new stats_default_page_values(); + returnstruct.all_scenes = m_scene.ToArray(); + lock (db) + { + string SQL = @"SELECT COUNT(DISTINCT agent_id) as agents, COUNT(*) as sessions, AVG(avg_fps) as client_fps, + AVG(avg_sim_fps) as savg_sim_fps, AVG(avg_ping) as sav_ping, SUM(n_out_kb) as num_in_kb, + SUM(n_out_pk) as num_in_packets, SUM(n_in_kb) as num_out_kb, SUM(n_in_pk) as num_out_packets, AVG(mem_use) as sav_mem_use + FROM stats_session_data;"; + SqliteCommand cmd = new SqliteCommand(SQL, db); + SqliteDataReader sdr = cmd.ExecuteReader(); + if (sdr.HasRows) + { + sdr.Read(); + returnstruct.total_num_users = Convert.ToInt32(sdr["agents"]); + returnstruct.total_num_sessions = Convert.ToInt32(sdr["sessions"]); + returnstruct.avg_client_fps = Convert.ToSingle(sdr["client_fps"]); + returnstruct.avg_sim_fps = Convert.ToSingle(sdr["savg_sim_fps"]); + returnstruct.avg_ping = Convert.ToSingle(sdr["sav_ping"]); + returnstruct.total_kb_out = Convert.ToSingle(sdr["num_out_kb"]); + returnstruct.total_kb_in = Convert.ToSingle(sdr["num_in_kb"]); + returnstruct.avg_client_mem_use = Convert.ToSingle(sdr["sav_mem_use"]); + + } + } + return returnstruct; + } + + /// + /// Return summar information in the form: + ///
+        /// {"totalUsers": "34",
+        ///  "totalSessions": "233",
+        ///  ...
+        /// }
+        /// 
+ ///
+ /// + /// + public string RenderJson(Hashtable pModelResult) { + stats_default_page_values values = (stats_default_page_values) pModelResult["hdata"]; + + OSDMap summaryInfo = new OSDMap(); + summaryInfo.Add("totalUsers", new OSDString(values.total_num_users.ToString())); + summaryInfo.Add("totalSessions", new OSDString(values.total_num_sessions.ToString())); + summaryInfo.Add("averageClientFPS", new OSDString(values.avg_client_fps.ToString())); + summaryInfo.Add("averageClientMem", new OSDString(values.avg_client_mem_use.ToString())); + summaryInfo.Add("averageSimFPS", new OSDString(values.avg_sim_fps.ToString())); + summaryInfo.Add("averagePingTime", new OSDString(values.avg_ping.ToString())); + summaryInfo.Add("totalKBOut", new OSDString(values.total_kb_out.ToString())); + summaryInfo.Add("totalKBIn", new OSDString(values.total_kb_in.ToString())); + return summaryInfo.ToString(); + } + } + + public struct stats_default_page_values + { + public int total_num_users; + public int total_num_sessions; + public float avg_client_fps; + public float avg_client_mem_use; + public float avg_sim_fps; + public float avg_ping; + public float total_kb_out; + public float total_kb_in; + public float avg_client_resends; + public Scene[] all_scenes; + public Dictionary sim_stat_data; + public Dictionary stats_reports; + } + +} diff --git a/OpenSim/Region/OptionalModules/UserStatistics/HTMLUtil.cs b/OpenSim/Region/OptionalModules/UserStatistics/HTMLUtil.cs new file mode 100644 index 0000000..c07619f --- /dev/null +++ b/OpenSim/Region/OptionalModules/UserStatistics/HTMLUtil.cs @@ -0,0 +1,263 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenSim.Region.UserStatistics +{ + public static class HTMLUtil + { + + public static void TR_O(ref StringBuilder o, string pclass) + { + o.Append(" 0) + { + GenericClass(ref o, pclass); + } + o.Append(">\n\t"); + } + + public static void TR_C(ref StringBuilder o) + { + o.Append("\n"); + } + + public static void TD_O(ref StringBuilder o, string pclass) + { + TD_O(ref o, pclass, 0, 0); + } + + public static void TD_O(ref StringBuilder o, string pclass, int rowspan, int colspan) + { + o.Append(" 0) + { + GenericClass(ref o, pclass); + } + if (rowspan > 1) + { + o.Append(" rowspan=\""); + o.Append(rowspan); + o.Append("\""); + } + if (colspan > 1) + { + o.Append(" colspan=\""); + o.Append(colspan); + o.Append("\""); + } + o.Append(">"); + } + + public static void TD_C(ref StringBuilder o) + { + o.Append(""); + } + + public static void TABLE_O(ref StringBuilder o, string pclass) + { + o.Append(" 0) + { + GenericClass(ref o, pclass); + } + o.Append(">\n\t"); + } + + public static void TABLE_C(ref StringBuilder o) + { + o.Append("\n"); + } + + public static void BLOCKQUOTE_O(ref StringBuilder o, string pclass) + { + o.Append(" 0) + { + GenericClass(ref o, pclass); + } + o.Append(" />\n"); + } + + public static void BLOCKQUOTE_C(ref StringBuilder o) + { + o.Append("\n"); + } + + public static void BR(ref StringBuilder o) + { + o.Append("
\n"); + } + + public static void HR(ref StringBuilder o, string pclass) + { + o.Append(" 0) + { + GenericClass(ref o, pclass); + } + o.Append(" />\n"); + } + + public static void UL_O(ref StringBuilder o, string pclass) + { + o.Append(" 0) + { + GenericClass(ref o, pclass); + } + o.Append(" />\n"); + } + + public static void UL_C(ref StringBuilder o) + { + o.Append("\n"); + } + + public static void OL_O(ref StringBuilder o, string pclass) + { + o.Append(" 0) + { + GenericClass(ref o, pclass); + } + o.Append(" />\n"); + } + + public static void OL_C(ref StringBuilder o) + { + o.Append("\n"); + } + + public static void LI_O(ref StringBuilder o, string pclass) + { + o.Append(" 0) + { + GenericClass(ref o, pclass); + } + o.Append(" />\n"); + } + + public static void LI_C(ref StringBuilder o) + { + o.Append("\n"); + } + + public static void GenericClass(ref StringBuilder o, string pclass) + { + o.Append(" class=\""); + o.Append(pclass); + o.Append("\""); + } + + public static void InsertProtoTypeAJAX(ref StringBuilder o) + { + o.Append("\n"); + o.Append("\n"); + } + + public static void InsertPeriodicUpdaters(ref StringBuilder o, string[] divID, int[] seconds, string[] reportfrag) + { + o.Append(""); + } + + public static void HtmlHeaders_O(ref StringBuilder o) + { + o.Append("\n"); + o.Append(""); + o.Append(""); + } + + public static void HtmlHeaders_C(ref StringBuilder o) + { + o.Append(""); + o.Append(""); + } + + public static void AddReportLinks(ref StringBuilder o, Dictionary reports, string pClass) + { + int repcount = 0; + foreach (string str in reports.Keys) + { + if (reports[str].ReportName.Length > 0) + { + if (repcount > 0) + { + o.Append("|  "); + } + A(ref o, reports[str].ReportName, str, pClass); + o.Append("  "); + repcount++; + } + } + } + + public static void A(ref StringBuilder o, string linktext, string linkhref, string pClass) + { + o.Append(" 0) + { + GenericClass(ref o, pClass); + } + o.Append(" href=\""); + o.Append(linkhref); + o.Append("\">"); + o.Append(linktext); + o.Append(""); + } + } +} diff --git a/OpenSim/Region/OptionalModules/UserStatistics/IStatsReport.cs b/OpenSim/Region/OptionalModules/UserStatistics/IStatsReport.cs new file mode 100644 index 0000000..80c4487 --- /dev/null +++ b/OpenSim/Region/OptionalModules/UserStatistics/IStatsReport.cs @@ -0,0 +1,39 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System.Collections; + +namespace OpenSim.Region.UserStatistics +{ + public interface IStatsController + { + string ReportName { get; } + Hashtable ProcessModel(Hashtable pParams); + string RenderView(Hashtable pModelResult); + string RenderJson(Hashtable pModelResult); + } +} diff --git a/OpenSim/Region/OptionalModules/UserStatistics/LogLinesAJAX.cs b/OpenSim/Region/OptionalModules/UserStatistics/LogLinesAJAX.cs new file mode 100644 index 0000000..4d45b80 --- /dev/null +++ b/OpenSim/Region/OptionalModules/UserStatistics/LogLinesAJAX.cs @@ -0,0 +1,159 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using Mono.Data.SqliteClient; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Framework.Monitoring; + +namespace OpenSim.Region.UserStatistics +{ + public class LogLinesAJAX : IStatsController + { + private Regex normalizeEndLines = new Regex(@"\r\n", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.Multiline); + + private Regex webFormat = new Regex(@"[^\s]*\s([^,]*),[^\s]*\s([A-Z]*)[^\s-][^\[]*\[([^\]]*)\]([^\n]*)", + RegexOptions.Singleline | RegexOptions.Compiled); + private Regex TitleColor = new Regex(@"[^\s]*\s(?:[^,]*),[^\s]*\s(?:[A-Z]*)[^\s-][^\[]*\[([^\]]*)\](?:[^\n]*)", + RegexOptions.Singleline | RegexOptions.Compiled); + + + #region IStatsController Members + + public string ReportName + { + get { return ""; } + } + + public Hashtable ProcessModel(Hashtable pParams) + { + Hashtable nh = new Hashtable(); + nh.Add("loglines", pParams["LogLines"]); + return nh; + } + + public string RenderView(Hashtable pModelResult) + { + StringBuilder output = new StringBuilder(); + + HTMLUtil.HR(ref output, ""); + output.Append("

ActiveLog

\n"); + + string tmp = normalizeEndLines.Replace(pModelResult["loglines"].ToString(), "\n"); + + string[] result = Regex.Split(tmp, "\n"); + + string formatopen = ""; + string formatclose = ""; + + for (int i = 0; i < result.Length; i++) + { + if (result[i].Length >= 30) + { + string logtype = result[i].Substring(24, 6); + switch (logtype) + { + case "WARN ": + formatopen = ""; + formatclose = ""; + break; + + case "ERROR ": + formatopen = ""; + formatclose = ""; + break; + + default: + formatopen = ""; + formatclose = ""; + break; + + } + } + StringBuilder replaceStr = new StringBuilder(); + //string titlecolorresults = + + string formatresult = Regex.Replace(TitleColor.Replace(result[i], "$1"), "[^ABCDEFabcdef0-9]", ""); + if (formatresult.Length > 6) + { + formatresult = formatresult.Substring(0, 6); + + } + for (int j = formatresult.Length; j <= 5; j++) + formatresult += "0"; + replaceStr.Append("$1 - [$3] $4
"); + string repstr = replaceStr.ToString(); + + output.Append(formatopen); + output.Append(webFormat.Replace(result[i], repstr)); + output.Append(formatclose); + } + + + return output.ToString(); + } + + /// + /// Return the last log lines. Output in the format: + ///
+        /// {"logLines": [
+        /// "line1",
+        /// "line2",
+        /// ...
+        /// ]
+        /// }
+        /// 
+ ///
+ /// + /// + public string RenderJson(Hashtable pModelResult) + { + OSDMap logInfo = new OpenMetaverse.StructuredData.OSDMap(); + + OSDArray logLines = new OpenMetaverse.StructuredData.OSDArray(); + string tmp = normalizeEndLines.Replace(pModelResult["loglines"].ToString(), "\n"); + string[] result = Regex.Split(tmp, "\n"); + for (int i = 0; i < result.Length; i++) + { + logLines.Add(new OSDString(result[i])); + } + logInfo.Add("logLines", logLines); + return logInfo.ToString(); + } + + #endregion + } +} diff --git a/OpenSim/Region/OptionalModules/UserStatistics/Prototype_distributor.cs b/OpenSim/Region/OptionalModules/UserStatistics/Prototype_distributor.cs new file mode 100644 index 0000000..6f8b2aa --- /dev/null +++ b/OpenSim/Region/OptionalModules/UserStatistics/Prototype_distributor.cs @@ -0,0 +1,80 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using OpenSim.Framework; + +namespace OpenSim.Region.UserStatistics +{ + public class Prototype_distributor : IStatsController + { + private string jsFileName = "prototype.js"; + private string prototypejs = string.Empty; + + public Prototype_distributor() + { + jsFileName = "prototype.js"; + } + + public Prototype_distributor(string jsName) + { + jsFileName = jsName; + } + + public string ReportName + { + get { return ""; } + } + public Hashtable ProcessModel(Hashtable pParams) + { + Hashtable pResult = new Hashtable(); + pResult["js"] = jsFileName; + return pResult; + } + + public string RenderView(Hashtable pModelResult) + { + string fileName = (string)pModelResult["js"]; + using (StreamReader fs = new StreamReader(new FileStream(Util.dataDir() + "/data/" + fileName, FileMode.Open))) + { + prototypejs = fs.ReadToEnd(); + fs.Close(); + } + return prototypejs; + } + + public string RenderJson(Hashtable pModelResult) + { + return "{}"; + } + + } +} diff --git a/OpenSim/Region/OptionalModules/UserStatistics/Sessions_Report.cs b/OpenSim/Region/OptionalModules/UserStatistics/Sessions_Report.cs new file mode 100644 index 0000000..0e94912 --- /dev/null +++ b/OpenSim/Region/OptionalModules/UserStatistics/Sessions_Report.cs @@ -0,0 +1,288 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using Mono.Data.SqliteClient; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Region.UserStatistics +{ + public class Sessions_Report : IStatsController + { + #region IStatsController Members + + public string ReportName + { + get { return "Sessions"; } + } + + public Hashtable ProcessModel(Hashtable pParams) + { + Hashtable modeldata = new Hashtable(); + modeldata.Add("Scenes", pParams["Scenes"]); + modeldata.Add("Reports", pParams["Reports"]); + SqliteConnection dbConn = (SqliteConnection)pParams["DatabaseConnection"]; + List lstSessions = new List(); + Hashtable requestvars = (Hashtable) pParams["RequestVars"]; + + + string puserUUID = string.Empty; + string clientVersionString = string.Empty; + int queryparams = 0; + + if (requestvars != null) + { + if (requestvars.ContainsKey("UserID")) + { + UUID testUUID = UUID.Zero; + if (UUID.TryParse(requestvars["UserID"].ToString(), out testUUID)) + { + puserUUID = requestvars["UserID"].ToString(); + + } + } + + if (requestvars.ContainsKey("VersionString")) + { + clientVersionString = requestvars["VersionString"].ToString(); + } + } + + lock (dbConn) + { + string sql = + "SELECT distinct a.name_f, a.name_l, a.Agent_ID, b.Session_ID, b.client_version, b.last_updated, b.start_time FROM stats_session_data a LEFT OUTER JOIN stats_session_data b ON a.Agent_ID = b.Agent_ID"; + + if (puserUUID.Length > 0) + { + if (queryparams == 0) + sql += " WHERE"; + else + sql += " AND"; + + sql += " b.agent_id=:agent_id"; + queryparams++; + } + + if (clientVersionString.Length > 0) + { + if (queryparams == 0) + sql += " WHERE"; + else + sql += " AND"; + + sql += " b.client_version=:client_version"; + queryparams++; + } + + sql += " ORDER BY a.name_f, a.name_l, b.last_updated;"; + + SqliteCommand cmd = new SqliteCommand(sql, dbConn); + + if (puserUUID.Length > 0) + cmd.Parameters.Add(new SqliteParameter(":agent_id", puserUUID)); + if (clientVersionString.Length > 0) + cmd.Parameters.Add(new SqliteParameter(":client_version", clientVersionString)); + + SqliteDataReader sdr = cmd.ExecuteReader(); + + if (sdr.HasRows) + { + UUID userUUID = UUID.Zero; + + SessionList activeSessionList = new SessionList(); + activeSessionList.user_id=UUID.Random(); + while (sdr.Read()) + { + UUID readUUID = UUID.Parse(sdr["agent_id"].ToString()); + if (readUUID != userUUID) + { + activeSessionList = new SessionList(); + activeSessionList.user_id = readUUID; + activeSessionList.firstname = sdr["name_f"].ToString(); + activeSessionList.lastname = sdr["name_l"].ToString(); + activeSessionList.sessions = new List(); + lstSessions.Add(activeSessionList); + } + + ShortSessionData ssd = new ShortSessionData(); + + ssd.last_update = Utils.UnixTimeToDateTime((uint)Convert.ToInt32(sdr["last_updated"])); + ssd.start_time = Utils.UnixTimeToDateTime((uint)Convert.ToInt32(sdr["start_time"])); + ssd.session_id = UUID.Parse(sdr["session_id"].ToString()); + ssd.client_version = sdr["client_version"].ToString(); + activeSessionList.sessions.Add(ssd); + + userUUID = activeSessionList.user_id; + } + } + sdr.Close(); + sdr.Dispose(); + + } + modeldata["SessionData"] = lstSessions; + return modeldata; + } + + public string RenderView(Hashtable pModelResult) + { + List lstSession = (List) pModelResult["SessionData"]; + Dictionary reports = (Dictionary)pModelResult["Reports"]; + + const string STYLESHEET = + @" + +"; + + StringBuilder output = new StringBuilder(); + HTMLUtil.HtmlHeaders_O(ref output); + output.Append(STYLESHEET); + HTMLUtil.HtmlHeaders_C(ref output); + + HTMLUtil.AddReportLinks(ref output, reports, ""); + + HTMLUtil.TABLE_O(ref output, "defaultr"); + HTMLUtil.TR_O(ref output, "defaultr"); + HTMLUtil.TD_O(ref output, "header"); + output.Append("FirstName"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "header"); + output.Append("LastName"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "header"); + output.Append("SessionEnd"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "header"); + output.Append("SessionLength"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "header"); + output.Append("Client"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + if (lstSession.Count == 0) + { + HTMLUtil.TR_O(ref output, ""); + HTMLUtil.TD_O(ref output, "align_top", 1, 5); + output.Append("No results for that query"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + } + foreach (SessionList ssnlst in lstSession) + { + int cnt = 0; + foreach (ShortSessionData sesdata in ssnlst.sessions) + { + HTMLUtil.TR_O(ref output, ""); + if (cnt++ == 0) + { + HTMLUtil.TD_O(ref output, "align_top", ssnlst.sessions.Count, 1); + output.Append(ssnlst.firstname); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "align_top", ssnlst.sessions.Count, 1); + output.Append(ssnlst.lastname); + HTMLUtil.TD_C(ref output); + } + HTMLUtil.TD_O(ref output, "content"); + output.Append(sesdata.last_update.ToShortDateString()); + output.Append(" - "); + output.Append(sesdata.last_update.ToShortTimeString()); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "content"); + TimeSpan dtlength = sesdata.last_update.Subtract(sesdata.start_time); + if (dtlength.Days > 0) + { + output.Append(dtlength.Days); + output.Append(" Days "); + } + if (dtlength.Hours > 0) + { + output.Append(dtlength.Hours); + output.Append(" Hours "); + } + if (dtlength.Minutes > 0) + { + output.Append(dtlength.Minutes); + output.Append(" Minutes"); + } + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "content"); + output.Append(sesdata.client_version); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + + } + HTMLUtil.TR_O(ref output, ""); + HTMLUtil.TD_O(ref output, "align_top", 1, 5); + HTMLUtil.HR(ref output, ""); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + } + HTMLUtil.TABLE_C(ref output); + output.Append("\n"); + return output.ToString(); + } + + public class SessionList + { + public string firstname; + public string lastname; + public UUID user_id; + public List sessions; + } + + public struct ShortSessionData + { + public UUID session_id; + public string client_version; + public DateTime last_update; + public DateTime start_time; + } + + public string RenderJson(Hashtable pModelResult) + { + return "{}"; + } + #endregion + } + +} diff --git a/OpenSim/Region/OptionalModules/UserStatistics/SimStatsAJAX.cs b/OpenSim/Region/OptionalModules/UserStatistics/SimStatsAJAX.cs new file mode 100644 index 0000000..06d9e91 --- /dev/null +++ b/OpenSim/Region/OptionalModules/UserStatistics/SimStatsAJAX.cs @@ -0,0 +1,276 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using Mono.Data.SqliteClient; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Framework.Monitoring; + +namespace OpenSim.Region.UserStatistics +{ + public class SimStatsAJAX : IStatsController + { + #region IStatsController Members + + public string ReportName + { + get { return ""; } + } + + public Hashtable ProcessModel(Hashtable pParams) + { + List m_scene = (List)pParams["Scenes"]; + + Hashtable nh = new Hashtable(); + nh.Add("hdata", m_scene); + nh.Add("simstats", pParams["SimStats"]); + return nh; + } + + public string RenderView(Hashtable pModelResult) + { + StringBuilder output = new StringBuilder(); + List all_scenes = (List) pModelResult["hdata"]; + Dictionary sdatadic = (Dictionary)pModelResult["simstats"]; + + const string TableClass = "defaultr"; + const string TRClass = "defaultr"; + const string TDHeaderClass = "header"; + const string TDDataClass = "content"; + //const string TDDataClassRight = "contentright"; + const string TDDataClassCenter = "contentcenter"; + + foreach (USimStatsData sdata in sdatadic.Values) + { + + + foreach (Scene sn in all_scenes) + { + if (sn.RegionInfo.RegionID == sdata.RegionId) + { + output.Append("

"); + output.Append(sn.RegionInfo.RegionName); + output.Append("

"); + } + } + HTMLUtil.TABLE_O(ref output, TableClass); + HTMLUtil.TR_O(ref output, TRClass); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("Dilatn"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("SimFPS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("PhysFPS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("AgntUp"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("RootAg"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("ChldAg"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("Prims"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("ATvPrm"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("AtvScr"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("ScrLPS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + HTMLUtil.TR_O(ref output, TRClass); + HTMLUtil.TD_O(ref output, TDDataClass); + output.Append(sdata.TimeDilation); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClass); + output.Append(sdata.SimFps); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.PhysicsFps); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.AgentUpdates); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.RootAgents); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.ChildAgents); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.TotalPrims); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.ActivePrims); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.ActiveScripts); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.ScriptLinesPerSecond); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + HTMLUtil.TR_O(ref output, TRClass); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("FrmMS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("AgtMS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("PhysMS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("OthrMS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("OutPPS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("InPPS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("NoAckKB"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("PndDWN"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("PndUP"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + HTMLUtil.TR_O(ref output, TRClass); + HTMLUtil.TD_O(ref output, TDDataClass); + output.Append(sdata.TotalFrameTime); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClass); + output.Append(sdata.AgentFrameTime); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.PhysicsFrameTime); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.OtherFrameTime); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.OutPacketsPerSecond); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.InPacketsPerSecond); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.UnackedBytes); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.PendingDownloads); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.PendingUploads); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + HTMLUtil.TABLE_C(ref output); + + } + + return output.ToString(); + } + + /// + /// Return stat information for all regions in the sim. Returns data of the form: + ///
+        /// {"REGIONNAME": {
+        ///     "region": "REGIONNAME",
+        ///     "timeDilation": "101", 
+        ///     ...     // the rest of the stat info
+        ///     },
+        ///  ...    // entries for each region
+        ///  }
+        /// 
+ ///
+ /// + /// + public string RenderJson(Hashtable pModelResult) + { + List all_scenes = (List) pModelResult["hdata"]; + Dictionary sdatadic = (Dictionary)pModelResult["simstats"]; + + OSDMap allStatsInfo = new OpenMetaverse.StructuredData.OSDMap(); + foreach (USimStatsData sdata in sdatadic.Values) + { + OSDMap statsInfo = new OpenMetaverse.StructuredData.OSDMap(); + string regionName = "unknown"; + foreach (Scene sn in all_scenes) + { + if (sn.RegionInfo.RegionID == sdata.RegionId) + { + regionName = sn.RegionInfo.RegionName; + break; + } + } + statsInfo.Add("region", new OSDString(regionName)); + statsInfo.Add("timeDilation", new OSDString(sdata.TimeDilation.ToString())); + statsInfo.Add("simFPS", new OSDString(sdata.SimFps.ToString())); + statsInfo.Add("physicsFPS", new OSDString(sdata.PhysicsFps.ToString())); + statsInfo.Add("agentUpdates", new OSDString(sdata.AgentUpdates.ToString())); + statsInfo.Add("rootAgents", new OSDString(sdata.RootAgents.ToString())); + statsInfo.Add("childAgents", new OSDString(sdata.ChildAgents.ToString())); + statsInfo.Add("totalPrims", new OSDString(sdata.TotalPrims.ToString())); + statsInfo.Add("activePrims", new OSDString(sdata.ActivePrims.ToString())); + statsInfo.Add("activeScripts", new OSDString(sdata.ActiveScripts.ToString())); + statsInfo.Add("scriptLinesPerSec", new OSDString(sdata.ScriptLinesPerSecond.ToString())); + statsInfo.Add("totalFrameTime", new OSDString(sdata.TotalFrameTime.ToString())); + statsInfo.Add("agentFrameTime", new OSDString(sdata.AgentFrameTime.ToString())); + statsInfo.Add("physicsFrameTime", new OSDString(sdata.PhysicsFrameTime.ToString())); + statsInfo.Add("otherFrameTime", new OSDString(sdata.OtherFrameTime.ToString())); + statsInfo.Add("outPacketsPerSec", new OSDString(sdata.OutPacketsPerSecond.ToString())); + statsInfo.Add("inPacketsPerSec", new OSDString(sdata.InPacketsPerSecond.ToString())); + statsInfo.Add("unackedByptes", new OSDString(sdata.UnackedBytes.ToString())); + statsInfo.Add("pendingDownloads", new OSDString(sdata.PendingDownloads.ToString())); + statsInfo.Add("pendingUploads", new OSDString(sdata.PendingUploads.ToString())); + + allStatsInfo.Add(regionName, statsInfo); + } + return allStatsInfo.ToString(); + } + + #endregion + } +} diff --git a/OpenSim/Region/OptionalModules/UserStatistics/Updater_distributor.cs b/OpenSim/Region/OptionalModules/UserStatistics/Updater_distributor.cs new file mode 100644 index 0000000..601e06b --- /dev/null +++ b/OpenSim/Region/OptionalModules/UserStatistics/Updater_distributor.cs @@ -0,0 +1,70 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using OpenSim.Framework; + +namespace OpenSim.Region.UserStatistics +{ + public class Updater_distributor : IStatsController + { + private string updaterjs = string.Empty; + + public string ReportName + { + get { return ""; } + } + + public Hashtable ProcessModel(Hashtable pParams) + { + Hashtable pResult = new Hashtable(); + if (updaterjs.Length == 0) + { + StreamReader fs = new StreamReader(new FileStream(Util.dataDir() + "/data/updater.js", FileMode.Open)); + updaterjs = fs.ReadToEnd(); + fs.Close(); + fs.Dispose(); + } + pResult["js"] = updaterjs; + return pResult; + } + + public string RenderView(Hashtable pModelResult) + { + return pModelResult["js"].ToString(); + } + + public string RenderJson(Hashtable pModelResult) { + return "{}"; + } + + } +} \ No newline at end of file diff --git a/OpenSim/Region/OptionalModules/UserStatistics/WebStatsModule.cs b/OpenSim/Region/OptionalModules/UserStatistics/WebStatsModule.cs new file mode 100644 index 0000000..bcb6361 --- /dev/null +++ b/OpenSim/Region/OptionalModules/UserStatistics/WebStatsModule.cs @@ -0,0 +1,1203 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Net; // to be used for REST-->Grid shortly +using System.Reflection; +using System.Text; +using System.Threading; +using log4net; +using Nini.Config; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Framework.Servers; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using Mono.Data.SqliteClient; +using Mono.Addins; + +using Caps = OpenSim.Framework.Capabilities.Caps; + +using OSD = OpenMetaverse.StructuredData.OSD; +using OSDMap = OpenMetaverse.StructuredData.OSDMap; + +namespace OpenSim.Region.UserStatistics +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "WebStatsModule")] + public class WebStatsModule : ISharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static SqliteConnection dbConn; + + /// + /// User statistics sessions keyed by agent ID + /// + private Dictionary m_sessions = new Dictionary(); + + private List m_scenes = new List(); + private Dictionary reports = new Dictionary(); + private Dictionary m_simstatsCounters = new Dictionary(); + private const int updateStatsMod = 6; + private int updateLogMod = 1; + private volatile int updateLogCounter = 0; + private volatile int concurrencyCounter = 0; + private bool enabled = false; + private string m_loglines = String.Empty; + private volatile int lastHit = 12000; + + #region ISharedRegionModule + + public virtual void Initialise(IConfigSource config) + { + IConfig cnfg = config.Configs["WebStats"]; + + if (cnfg != null) + enabled = cnfg.GetBoolean("enabled", false); + } + + public virtual void PostInitialise() + { + if (!enabled) + return; + + if (Util.IsWindows()) + Util.LoadArchSpecificWindowsDll("sqlite3.dll"); + + //IConfig startupConfig = config.Configs["Startup"]; + + dbConn = new SqliteConnection("URI=file:LocalUserStatistics.db,version=3"); + dbConn.Open(); + CreateTables(dbConn); + + Prototype_distributor protodep = new Prototype_distributor(); + Updater_distributor updatedep = new Updater_distributor(); + ActiveConnectionsAJAX ajConnections = new ActiveConnectionsAJAX(); + SimStatsAJAX ajSimStats = new SimStatsAJAX(); + LogLinesAJAX ajLogLines = new LogLinesAJAX(); + Default_Report defaultReport = new Default_Report(); + Clients_report clientReport = new Clients_report(); + Sessions_Report sessionsReport = new Sessions_Report(); + + reports.Add("prototype.js", protodep); + reports.Add("updater.js", updatedep); + reports.Add("activeconnectionsajax.html", ajConnections); + reports.Add("simstatsajax.html", ajSimStats); + reports.Add("activelogajax.html", ajLogLines); + reports.Add("default.report", defaultReport); + reports.Add("clients.report", clientReport); + reports.Add("sessions.report", sessionsReport); + + reports.Add("sim.css", new Prototype_distributor("sim.css")); + reports.Add("sim.html", new Prototype_distributor("sim.html")); + reports.Add("jquery.js", new Prototype_distributor("jquery.js")); + + //// + // Add Your own Reports here (Do Not Modify Lines here Devs!) + //// + + //// + // End Own reports section + //// + + MainServer.Instance.AddHTTPHandler("/SStats/", HandleStatsRequest); + MainServer.Instance.AddHTTPHandler("/CAPS/VS/", HandleUnknownCAPSRequest); + } + + public virtual void AddRegion(Scene scene) + { + if (!enabled) + return; + + lock (m_scenes) + { + m_scenes.Add(scene); + updateLogMod = m_scenes.Count * 2; + + m_simstatsCounters.Add(scene.RegionInfo.RegionID, new USimStatsData(scene.RegionInfo.RegionID)); + + scene.EventManager.OnRegisterCaps += OnRegisterCaps; + scene.EventManager.OnDeregisterCaps += OnDeRegisterCaps; + scene.EventManager.OnClientClosed += OnClientClosed; + scene.EventManager.OnMakeRootAgent += OnMakeRootAgent; + scene.StatsReporter.OnSendStatsResult += ReceiveClassicSimStatsPacket; + } + } + + public void RegionLoaded(Scene scene) + { + } + + public void RemoveRegion(Scene scene) + { + if (!enabled) + return; + + lock (m_scenes) + { + m_scenes.Remove(scene); + updateLogMod = m_scenes.Count * 2; + m_simstatsCounters.Remove(scene.RegionInfo.RegionID); + } + } + + public virtual void Close() + { + if (!enabled) + return; + + dbConn.Close(); + dbConn.Dispose(); + m_sessions.Clear(); + m_scenes.Clear(); + reports.Clear(); + m_simstatsCounters.Clear(); + } + + public virtual string Name + { + get { return "ViewerStatsModule"; } + } + + public Type ReplaceableInterface + { + get { return null; } + } + + #endregion + + private void ReceiveClassicSimStatsPacket(SimStats stats) + { + if (!enabled) + return; + + try + { + // Ignore the update if there's a report running right now + // ignore the update if there hasn't been a hit in 30 seconds. + if (concurrencyCounter > 0 || System.Environment.TickCount - lastHit > 30000) + return; + + // We will conduct this under lock so that fields such as updateLogCounter do not potentially get + // confused if a scene is removed. + // XXX: Possibly the scope of this lock could be reduced though it's not critical. + lock (m_scenes) + { + if (updateLogMod != 0 && updateLogCounter++ % updateLogMod == 0) + { + m_loglines = readLogLines(10); + + if (updateLogCounter > 10000) + updateLogCounter = 1; + } + + USimStatsData ss = m_simstatsCounters[stats.RegionUUID]; + + if ((++ss.StatsCounter % updateStatsMod) == 0) + { + ss.ConsumeSimStats(stats); + } + } + } + catch (KeyNotFoundException) + { + } + } + + private Hashtable HandleUnknownCAPSRequest(Hashtable request) + { + //string regpath = request["uri"].ToString(); + int response_code = 200; + string contenttype = "text/html"; + UpdateUserStats(ParseViewerStats(request["body"].ToString(), UUID.Zero), dbConn); + Hashtable responsedata = new Hashtable(); + + responsedata["int_response_code"] = response_code; + responsedata["content_type"] = contenttype; + responsedata["keepalive"] = false; + responsedata["str_response_string"] = string.Empty; + return responsedata; + } + + private Hashtable HandleStatsRequest(Hashtable request) + { + lastHit = System.Environment.TickCount; + Hashtable responsedata = new Hashtable(); + string regpath = request["uri"].ToString(); + int response_code = 404; + string contenttype = "text/html"; + bool jsonFormatOutput = false; + + string strOut = string.Empty; + + // The request patch should be "/SStats/reportName" where 'reportName' + // is one of the names added to the 'reports' hashmap. + regpath = regpath.Remove(0, 8); + if (regpath.Length == 0) regpath = "default.report"; + if (reports.ContainsKey(regpath)) + { + IStatsController rep = reports[regpath]; + Hashtable repParams = new Hashtable(); + + if (request.ContainsKey("json")) + jsonFormatOutput = true; + + if (request.ContainsKey("requestvars")) + repParams["RequestVars"] = request["requestvars"]; + else + repParams["RequestVars"] = new Hashtable(); + + if (request.ContainsKey("querystringkeys")) + repParams["QueryStringKeys"] = request["querystringkeys"]; + else + repParams["QueryStringKeys"] = new string[0]; + + + repParams["DatabaseConnection"] = dbConn; + repParams["Scenes"] = m_scenes; + repParams["SimStats"] = m_simstatsCounters; + repParams["LogLines"] = m_loglines; + repParams["Reports"] = reports; + + concurrencyCounter++; + + if (jsonFormatOutput) + { + strOut = rep.RenderJson(rep.ProcessModel(repParams)); + contenttype = "text/json"; + } + else + { + strOut = rep.RenderView(rep.ProcessModel(repParams)); + } + + if (regpath.EndsWith("js")) + { + contenttype = "text/javascript"; + } + + if (regpath.EndsWith("css")) + { + contenttype = "text/css"; + } + + concurrencyCounter--; + + response_code = 200; + } + else + { + strOut = MainServer.Instance.GetHTTP404(""); + } + + responsedata["int_response_code"] = response_code; + responsedata["content_type"] = contenttype; + responsedata["keepalive"] = false; + responsedata["str_response_string"] = strOut; + + return responsedata; + } + + private void CreateTables(SqliteConnection db) + { + using (SqliteCommand createcmd = new SqliteCommand(SQL_STATS_TABLE_CREATE, db)) + { + createcmd.ExecuteNonQuery(); + } + } + + private void OnRegisterCaps(UUID agentID, Caps caps) + { +// m_log.DebugFormat("[WEB STATS MODULE]: OnRegisterCaps: agentID {0} caps {1}", agentID, caps); + + string capsPath = "/CAPS/VS/" + UUID.Random(); + caps.RegisterHandler( + "ViewerStats", + new RestStreamHandler( + "POST", + capsPath, + (request, path, param, httpRequest, httpResponse) + => ViewerStatsReport(request, path, param, agentID, caps), + "ViewerStats", + agentID.ToString())); + } + + private void OnDeRegisterCaps(UUID agentID, Caps caps) + { + } + + protected virtual void AddEventHandlers() + { + lock (m_scenes) + { + updateLogMod = m_scenes.Count * 2; + foreach (Scene scene in m_scenes) + { + scene.EventManager.OnRegisterCaps += OnRegisterCaps; + scene.EventManager.OnDeregisterCaps += OnDeRegisterCaps; + scene.EventManager.OnClientClosed += OnClientClosed; + scene.EventManager.OnMakeRootAgent += OnMakeRootAgent; + } + } + } + + private void OnMakeRootAgent(ScenePresence agent) + { +// m_log.DebugFormat( +// "[WEB STATS MODULE]: Looking for session {0} for {1} in {2}", +// agent.ControllingClient.SessionId, agent.Name, agent.Scene.Name); + + lock (m_sessions) + { + UserSession uid; + + if (!m_sessions.ContainsKey(agent.UUID)) + { + UserSessionData usd = UserSessionUtil.newUserSessionData(); + uid = new UserSession(); + uid.name_f = agent.Firstname; + uid.name_l = agent.Lastname; + uid.session_data = usd; + + m_sessions.Add(agent.UUID, uid); + } + else + { + uid = m_sessions[agent.UUID]; + } + + uid.region_id = agent.Scene.RegionInfo.RegionID; + uid.session_id = agent.ControllingClient.SessionId; + } + } + + private void OnClientClosed(UUID agentID, Scene scene) + { + lock (m_sessions) + { + if (m_sessions.ContainsKey(agentID) && m_sessions[agentID].region_id == scene.RegionInfo.RegionID) + { + m_sessions.Remove(agentID); + } + } + } + + private string readLogLines(int amount) + { + Encoding encoding = Encoding.ASCII; + int sizeOfChar = encoding.GetByteCount("\n"); + byte[] buffer = encoding.GetBytes("\n"); + string logfile = Util.logFile(); + FileStream fs = new FileStream(logfile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + Int64 tokenCount = 0; + Int64 endPosition = fs.Length / sizeOfChar; + + for (Int64 position = sizeOfChar; position < endPosition; position += sizeOfChar) + { + fs.Seek(-position, SeekOrigin.End); + fs.Read(buffer, 0, buffer.Length); + + if (encoding.GetString(buffer) == "\n") + { + tokenCount++; + if (tokenCount == amount) + { + byte[] returnBuffer = new byte[fs.Length - fs.Position]; + fs.Read(returnBuffer, 0, returnBuffer.Length); + fs.Close(); + fs.Dispose(); + return encoding.GetString(returnBuffer); + } + } + } + + // handle case where number of tokens in file is less than numberOfTokens + fs.Seek(0, SeekOrigin.Begin); + buffer = new byte[fs.Length]; + fs.Read(buffer, 0, buffer.Length); + fs.Close(); + fs.Dispose(); + return encoding.GetString(buffer); + } + + /// + /// Callback for a viewerstats cap + /// + /// + /// + /// + /// + /// + /// + private string ViewerStatsReport(string request, string path, string param, + UUID agentID, Caps caps) + { +// m_log.DebugFormat("[WEB STATS MODULE]: Received viewer starts report from {0}", agentID); + + UpdateUserStats(ParseViewerStats(request, agentID), dbConn); + + return String.Empty; + } + + private UserSession ParseViewerStats(string request, UUID agentID) + { + UserSession uid = new UserSession(); + UserSessionData usd; + OSD message = OSDParser.DeserializeLLSDXml(request); + OSDMap mmap; + + lock (m_sessions) + { + if (agentID != UUID.Zero) + { + if (!m_sessions.ContainsKey(agentID)) + { + m_log.WarnFormat("[WEB STATS MODULE]: no session for stat disclosure for agent {0}", agentID); + return new UserSession(); + } + + uid = m_sessions[agentID]; + +// m_log.DebugFormat("[WEB STATS MODULE]: Got session {0} for {1}", uid.session_id, agentID); + } + else + { + // parse through the beginning to locate the session + if (message.Type != OSDType.Map) + return new UserSession(); + + mmap = (OSDMap)message; + { + UUID sessionID = mmap["session_id"].AsUUID(); + + if (sessionID == UUID.Zero) + return new UserSession(); + + + // search through each session looking for the owner + foreach (UUID usersessionid in m_sessions.Keys) + { + // got it! + if (m_sessions[usersessionid].session_id == sessionID) + { + agentID = usersessionid; + uid = m_sessions[usersessionid]; + break; + } + + } + + // can't find a session + if (agentID == UUID.Zero) + { + return new UserSession(); + } + } + } + } + + usd = uid.session_data; + + if (message.Type != OSDType.Map) + return new UserSession(); + + mmap = (OSDMap)message; + { + if (mmap["agent"].Type != OSDType.Map) + return new UserSession(); + OSDMap agent_map = (OSDMap)mmap["agent"]; + usd.agent_id = agentID; + usd.name_f = uid.name_f; + usd.name_l = uid.name_l; + usd.region_id = uid.region_id; + usd.a_language = agent_map["language"].AsString(); + usd.mem_use = (float)agent_map["mem_use"].AsReal(); + usd.meters_traveled = (float)agent_map["meters_traveled"].AsReal(); + usd.regions_visited = agent_map["regions_visited"].AsInteger(); + usd.run_time = (float)agent_map["run_time"].AsReal(); + usd.start_time = (float)agent_map["start_time"].AsReal(); + usd.client_version = agent_map["version"].AsString(); + + UserSessionUtil.UpdateMultiItems(ref usd, agent_map["agents_in_view"].AsInteger(), + (float)agent_map["ping"].AsReal(), + (float)agent_map["sim_fps"].AsReal(), + (float)agent_map["fps"].AsReal()); + + if (mmap["downloads"].Type != OSDType.Map) + return new UserSession(); + OSDMap downloads_map = (OSDMap)mmap["downloads"]; + usd.d_object_kb = (float)downloads_map["object_kbytes"].AsReal(); + usd.d_texture_kb = (float)downloads_map["texture_kbytes"].AsReal(); + usd.d_world_kb = (float)downloads_map["workd_kbytes"].AsReal(); + +// m_log.DebugFormat("[WEB STATS MODULE]: mmap[\"session_id\"] = [{0}]", mmap["session_id"].AsUUID()); + + usd.session_id = mmap["session_id"].AsUUID(); + + if (mmap["system"].Type != OSDType.Map) + return new UserSession(); + OSDMap system_map = (OSDMap)mmap["system"]; + + usd.s_cpu = system_map["cpu"].AsString(); + usd.s_gpu = system_map["gpu"].AsString(); + usd.s_os = system_map["os"].AsString(); + usd.s_ram = system_map["ram"].AsInteger(); + + if (mmap["stats"].Type != OSDType.Map) + return new UserSession(); + + OSDMap stats_map = (OSDMap)mmap["stats"]; + { + + if (stats_map["failures"].Type != OSDType.Map) + return new UserSession(); + OSDMap stats_failures = (OSDMap)stats_map["failures"]; + usd.f_dropped = stats_failures["dropped"].AsInteger(); + usd.f_failed_resends = stats_failures["failed_resends"].AsInteger(); + usd.f_invalid = stats_failures["invalid"].AsInteger(); + usd.f_resent = stats_failures["resent"].AsInteger(); + usd.f_send_packet = stats_failures["send_packet"].AsInteger(); + + if (stats_map["net"].Type != OSDType.Map) + return new UserSession(); + OSDMap stats_net = (OSDMap)stats_map["net"]; + { + if (stats_net["in"].Type != OSDType.Map) + return new UserSession(); + + OSDMap net_in = (OSDMap)stats_net["in"]; + usd.n_in_kb = (float)net_in["kbytes"].AsReal(); + usd.n_in_pk = net_in["packets"].AsInteger(); + + if (stats_net["out"].Type != OSDType.Map) + return new UserSession(); + OSDMap net_out = (OSDMap)stats_net["out"]; + + usd.n_out_kb = (float)net_out["kbytes"].AsReal(); + usd.n_out_pk = net_out["packets"].AsInteger(); + } + } + } + + uid.session_data = usd; + m_sessions[agentID] = uid; + +// m_log.DebugFormat( +// "[WEB STATS MODULE]: Parse data for {0} {1}, session {2}", uid.name_f, uid.name_l, uid.session_id); + + return uid; + } + + private void UpdateUserStats(UserSession uid, SqliteConnection db) + { +// m_log.DebugFormat( +// "[WEB STATS MODULE]: Updating user stats for {0} {1}, session {2}", uid.name_f, uid.name_l, uid.session_id); + + if (uid.session_id == UUID.Zero) + return; + + lock (db) + { + using (SqliteCommand updatecmd = new SqliteCommand(SQL_STATS_TABLE_INSERT, db)) + { + updatecmd.Parameters.Add(new SqliteParameter(":session_id", uid.session_data.session_id.ToString())); + updatecmd.Parameters.Add(new SqliteParameter(":agent_id", uid.session_data.agent_id.ToString())); + updatecmd.Parameters.Add(new SqliteParameter(":region_id", uid.session_data.region_id.ToString())); + updatecmd.Parameters.Add(new SqliteParameter(":last_updated", (int) uid.session_data.last_updated)); + updatecmd.Parameters.Add(new SqliteParameter(":remote_ip", uid.session_data.remote_ip)); + updatecmd.Parameters.Add(new SqliteParameter(":name_f", uid.session_data.name_f)); + updatecmd.Parameters.Add(new SqliteParameter(":name_l", uid.session_data.name_l)); + updatecmd.Parameters.Add(new SqliteParameter(":avg_agents_in_view", uid.session_data.avg_agents_in_view)); + updatecmd.Parameters.Add(new SqliteParameter(":min_agents_in_view", + (int) uid.session_data.min_agents_in_view)); + updatecmd.Parameters.Add(new SqliteParameter(":max_agents_in_view", + (int) uid.session_data.max_agents_in_view)); + updatecmd.Parameters.Add(new SqliteParameter(":mode_agents_in_view", + (int) uid.session_data.mode_agents_in_view)); + updatecmd.Parameters.Add(new SqliteParameter(":avg_fps", uid.session_data.avg_fps)); + updatecmd.Parameters.Add(new SqliteParameter(":min_fps", uid.session_data.min_fps)); + updatecmd.Parameters.Add(new SqliteParameter(":max_fps", uid.session_data.max_fps)); + updatecmd.Parameters.Add(new SqliteParameter(":mode_fps", uid.session_data.mode_fps)); + updatecmd.Parameters.Add(new SqliteParameter(":a_language", uid.session_data.a_language)); + updatecmd.Parameters.Add(new SqliteParameter(":mem_use", uid.session_data.mem_use)); + updatecmd.Parameters.Add(new SqliteParameter(":meters_traveled", uid.session_data.meters_traveled)); + updatecmd.Parameters.Add(new SqliteParameter(":avg_ping", uid.session_data.avg_ping)); + updatecmd.Parameters.Add(new SqliteParameter(":min_ping", uid.session_data.min_ping)); + updatecmd.Parameters.Add(new SqliteParameter(":max_ping", uid.session_data.max_ping)); + updatecmd.Parameters.Add(new SqliteParameter(":mode_ping", uid.session_data.mode_ping)); + updatecmd.Parameters.Add(new SqliteParameter(":regions_visited", uid.session_data.regions_visited)); + updatecmd.Parameters.Add(new SqliteParameter(":run_time", uid.session_data.run_time)); + updatecmd.Parameters.Add(new SqliteParameter(":avg_sim_fps", uid.session_data.avg_sim_fps)); + updatecmd.Parameters.Add(new SqliteParameter(":min_sim_fps", uid.session_data.min_sim_fps)); + updatecmd.Parameters.Add(new SqliteParameter(":max_sim_fps", uid.session_data.max_sim_fps)); + updatecmd.Parameters.Add(new SqliteParameter(":mode_sim_fps", uid.session_data.mode_sim_fps)); + updatecmd.Parameters.Add(new SqliteParameter(":start_time", uid.session_data.start_time)); + updatecmd.Parameters.Add(new SqliteParameter(":client_version", uid.session_data.client_version)); + updatecmd.Parameters.Add(new SqliteParameter(":s_cpu", uid.session_data.s_cpu)); + updatecmd.Parameters.Add(new SqliteParameter(":s_gpu", uid.session_data.s_gpu)); + updatecmd.Parameters.Add(new SqliteParameter(":s_os", uid.session_data.s_os)); + updatecmd.Parameters.Add(new SqliteParameter(":s_ram", uid.session_data.s_ram)); + updatecmd.Parameters.Add(new SqliteParameter(":d_object_kb", uid.session_data.d_object_kb)); + updatecmd.Parameters.Add(new SqliteParameter(":d_texture_kb", uid.session_data.d_texture_kb)); + updatecmd.Parameters.Add(new SqliteParameter(":d_world_kb", uid.session_data.d_world_kb)); + updatecmd.Parameters.Add(new SqliteParameter(":n_in_kb", uid.session_data.n_in_kb)); + updatecmd.Parameters.Add(new SqliteParameter(":n_in_pk", uid.session_data.n_in_pk)); + updatecmd.Parameters.Add(new SqliteParameter(":n_out_kb", uid.session_data.n_out_kb)); + updatecmd.Parameters.Add(new SqliteParameter(":n_out_pk", uid.session_data.n_out_pk)); + updatecmd.Parameters.Add(new SqliteParameter(":f_dropped", uid.session_data.f_dropped)); + updatecmd.Parameters.Add(new SqliteParameter(":f_failed_resends", uid.session_data.f_failed_resends)); + updatecmd.Parameters.Add(new SqliteParameter(":f_invalid", uid.session_data.f_invalid)); + updatecmd.Parameters.Add(new SqliteParameter(":f_off_circuit", uid.session_data.f_off_circuit)); + updatecmd.Parameters.Add(new SqliteParameter(":f_resent", uid.session_data.f_resent)); + updatecmd.Parameters.Add(new SqliteParameter(":f_send_packet", uid.session_data.f_send_packet)); + +// StringBuilder parameters = new StringBuilder(); +// SqliteParameterCollection spc = updatecmd.Parameters; +// foreach (SqliteParameter sp in spc) +// parameters.AppendFormat("{0}={1},", sp.ParameterName, sp.Value); +// +// m_log.DebugFormat("[WEB STATS MODULE]: Parameters {0}", parameters); + +// m_log.DebugFormat("[WEB STATS MODULE]: Database stats update for {0}", uid.session_data.agent_id); + + updatecmd.ExecuteNonQuery(); + } + } + } + + #region SQL + private const string SQL_STATS_TABLE_CREATE = @"CREATE TABLE IF NOT EXISTS stats_session_data ( + session_id VARCHAR(36) NOT NULL PRIMARY KEY, + agent_id VARCHAR(36) NOT NULL DEFAULT '', + region_id VARCHAR(36) NOT NULL DEFAULT '', + last_updated INT NOT NULL DEFAULT '0', + remote_ip VARCHAR(16) NOT NULL DEFAULT '', + name_f VARCHAR(50) NOT NULL DEFAULT '', + name_l VARCHAR(50) NOT NULL DEFAULT '', + avg_agents_in_view FLOAT NOT NULL DEFAULT '0', + min_agents_in_view INT NOT NULL DEFAULT '0', + max_agents_in_view INT NOT NULL DEFAULT '0', + mode_agents_in_view INT NOT NULL DEFAULT '0', + avg_fps FLOAT NOT NULL DEFAULT '0', + min_fps FLOAT NOT NULL DEFAULT '0', + max_fps FLOAT NOT NULL DEFAULT '0', + mode_fps FLOAT NOT NULL DEFAULT '0', + a_language VARCHAR(25) NOT NULL DEFAULT '', + mem_use FLOAT NOT NULL DEFAULT '0', + meters_traveled FLOAT NOT NULL DEFAULT '0', + avg_ping FLOAT NOT NULL DEFAULT '0', + min_ping FLOAT NOT NULL DEFAULT '0', + max_ping FLOAT NOT NULL DEFAULT '0', + mode_ping FLOAT NOT NULL DEFAULT '0', + regions_visited INT NOT NULL DEFAULT '0', + run_time FLOAT NOT NULL DEFAULT '0', + avg_sim_fps FLOAT NOT NULL DEFAULT '0', + min_sim_fps FLOAT NOT NULL DEFAULT '0', + max_sim_fps FLOAT NOT NULL DEFAULT '0', + mode_sim_fps FLOAT NOT NULL DEFAULT '0', + start_time FLOAT NOT NULL DEFAULT '0', + client_version VARCHAR(255) NOT NULL DEFAULT '', + s_cpu VARCHAR(255) NOT NULL DEFAULT '', + s_gpu VARCHAR(255) NOT NULL DEFAULT '', + s_os VARCHAR(2255) NOT NULL DEFAULT '', + s_ram INT NOT NULL DEFAULT '0', + d_object_kb FLOAT NOT NULL DEFAULT '0', + d_texture_kb FLOAT NOT NULL DEFAULT '0', + d_world_kb FLOAT NOT NULL DEFAULT '0', + n_in_kb FLOAT NOT NULL DEFAULT '0', + n_in_pk INT NOT NULL DEFAULT '0', + n_out_kb FLOAT NOT NULL DEFAULT '0', + n_out_pk INT NOT NULL DEFAULT '0', + f_dropped INT NOT NULL DEFAULT '0', + f_failed_resends INT NOT NULL DEFAULT '0', + f_invalid INT NOT NULL DEFAULT '0', + f_off_circuit INT NOT NULL DEFAULT '0', + f_resent INT NOT NULL DEFAULT '0', + f_send_packet INT NOT NULL DEFAULT '0' + );"; + + private const string SQL_STATS_TABLE_INSERT = @"INSERT OR REPLACE INTO stats_session_data ( +session_id, agent_id, region_id, last_updated, remote_ip, name_f, name_l, avg_agents_in_view, min_agents_in_view, max_agents_in_view, +mode_agents_in_view, avg_fps, min_fps, max_fps, mode_fps, a_language, mem_use, meters_traveled, avg_ping, min_ping, max_ping, mode_ping, +regions_visited, run_time, avg_sim_fps, min_sim_fps, max_sim_fps, mode_sim_fps, start_time, client_version, s_cpu, s_gpu, s_os, s_ram, +d_object_kb, d_texture_kb, d_world_kb, n_in_kb, n_in_pk, n_out_kb, n_out_pk, f_dropped, f_failed_resends, f_invalid, f_off_circuit, +f_resent, f_send_packet +) +VALUES +( +:session_id, :agent_id, :region_id, :last_updated, :remote_ip, :name_f, :name_l, :avg_agents_in_view, :min_agents_in_view, :max_agents_in_view, +:mode_agents_in_view, :avg_fps, :min_fps, :max_fps, :mode_fps, :a_language, :mem_use, :meters_traveled, :avg_ping, :min_ping, :max_ping, :mode_ping, +:regions_visited, :run_time, :avg_sim_fps, :min_sim_fps, :max_sim_fps, :mode_sim_fps, :start_time, :client_version, :s_cpu, :s_gpu, :s_os, :s_ram, +:d_object_kb, :d_texture_kb, :d_world_kb, :n_in_kb, :n_in_pk, :n_out_kb, :n_out_pk, :f_dropped, :f_failed_resends, :f_invalid, :f_off_circuit, +:f_resent, :f_send_packet +) +"; + + #endregion + + } + + public static class UserSessionUtil + { + public static UserSessionData newUserSessionData() + { + UserSessionData obj = ZeroSession(new UserSessionData()); + return obj; + } + + public static void UpdateMultiItems(ref UserSessionData s, int agents_in_view, float ping, float sim_fps, float fps) + { + // don't insert zero values here or it'll skew the statistics. + if (agents_in_view == 0 && fps == 0 && sim_fps == 0 && ping == 0) + return; + s._agents_in_view.Add(agents_in_view); + s._fps.Add(fps); + s._sim_fps.Add(sim_fps); + s._ping.Add(ping); + + int[] __agents_in_view = s._agents_in_view.ToArray(); + + s.avg_agents_in_view = ArrayAvg_i(__agents_in_view); + s.min_agents_in_view = ArrayMin_i(__agents_in_view); + s.max_agents_in_view = ArrayMax_i(__agents_in_view); + s.mode_agents_in_view = ArrayMode_i(__agents_in_view); + + float[] __fps = s._fps.ToArray(); + s.avg_fps = ArrayAvg_f(__fps); + s.min_fps = ArrayMin_f(__fps); + s.max_fps = ArrayMax_f(__fps); + s.mode_fps = ArrayMode_f(__fps); + + float[] __sim_fps = s._sim_fps.ToArray(); + s.avg_sim_fps = ArrayAvg_f(__sim_fps); + s.min_sim_fps = ArrayMin_f(__sim_fps); + s.max_sim_fps = ArrayMax_f(__sim_fps); + s.mode_sim_fps = ArrayMode_f(__sim_fps); + + float[] __ping = s._ping.ToArray(); + s.avg_ping = ArrayAvg_f(__ping); + s.min_ping = ArrayMin_f(__ping); + s.max_ping = ArrayMax_f(__ping); + s.mode_ping = ArrayMode_f(__ping); + } + + #region Statistics + + public static int ArrayMin_i(int[] arr) + { + int cnt = arr.Length; + if (cnt == 0) + return 0; + + Array.Sort(arr); + return arr[0]; + } + + public static int ArrayMax_i(int[] arr) + { + int cnt = arr.Length; + if (cnt == 0) + return 0; + + Array.Sort(arr); + return arr[cnt-1]; + } + + public static float ArrayMin_f(float[] arr) + { + int cnt = arr.Length; + if (cnt == 0) + return 0; + + Array.Sort(arr); + return arr[0]; + } + + public static float ArrayMax_f(float[] arr) + { + int cnt = arr.Length; + if (cnt == 0) + return 0; + + Array.Sort(arr); + return arr[cnt - 1]; + } + + public static float ArrayAvg_i(int[] arr) + { + int cnt = arr.Length; + + if (cnt == 0) + return 0; + + float result = arr[0]; + + for (int i = 1; i < cnt; i++) + result += arr[i]; + + return result / cnt; + } + + public static float ArrayAvg_f(float[] arr) + { + int cnt = arr.Length; + + if (cnt == 0) + return 0; + + float result = arr[0]; + + for (int i = 1; i < cnt; i++) + result += arr[i]; + + return result / cnt; + } + + public static float ArrayMode_f(float[] arr) + { + List mode = new List(); + + float[] srtArr = new float[arr.Length]; + float[,] freq = new float[arr.Length, 2]; + Array.Copy(arr, srtArr, arr.Length); + Array.Sort(srtArr); + + float tmp = srtArr[0]; + int index = 0; + int i = 0; + while (i < srtArr.Length) + { + freq[index, 0] = tmp; + + while (tmp == srtArr[i]) + { + freq[index, 1]++; + i++; + + if (i > srtArr.Length - 1) + break; + } + + if (i < srtArr.Length) + { + tmp = srtArr[i]; + index++; + } + + } + + Array.Clear(srtArr, 0, srtArr.Length); + + for (i = 0; i < srtArr.Length; i++) + srtArr[i] = freq[i, 1]; + + Array.Sort(srtArr); + + if ((srtArr[srtArr.Length - 1]) == 0 || (srtArr[srtArr.Length - 1]) == 1) + return 0; + + float freqtest = (float)freq.Length / freq.Rank; + + for (i = 0; i < freqtest; i++) + { + if (freq[i, 1] == srtArr[index]) + mode.Add(freq[i, 0]); + + } + + return mode.ToArray()[0]; + } + + public static int ArrayMode_i(int[] arr) + { + List mode = new List(); + + int[] srtArr = new int[arr.Length]; + int[,] freq = new int[arr.Length, 2]; + Array.Copy(arr, srtArr, arr.Length); + Array.Sort(srtArr); + + int tmp = srtArr[0]; + int index = 0; + int i = 0; + while (i < srtArr.Length) + { + freq[index, 0] = tmp; + + while (tmp == srtArr[i]) + { + freq[index, 1]++; + i++; + + if (i > srtArr.Length - 1) + break; + } + + if (i < srtArr.Length) + { + tmp = srtArr[i]; + index++; + } + + } + + Array.Clear(srtArr, 0, srtArr.Length); + + for (i = 0; i < srtArr.Length; i++) + srtArr[i] = freq[i, 1]; + + Array.Sort(srtArr); + + if ((srtArr[srtArr.Length - 1]) == 0 || (srtArr[srtArr.Length - 1]) == 1) + return 0; + + float freqtest = (float)freq.Length / freq.Rank; + + for (i = 0; i < freqtest; i++) + { + if (freq[i, 1] == srtArr[index]) + mode.Add(freq[i, 0]); + + } + + return mode.ToArray()[0]; + } + + #endregion + + private static UserSessionData ZeroSession(UserSessionData s) + { + s.session_id = UUID.Zero; + s.agent_id = UUID.Zero; + s.region_id = UUID.Zero; + s.last_updated = Util.UnixTimeSinceEpoch(); + s.remote_ip = ""; + s.name_f = ""; + s.name_l = ""; + s.avg_agents_in_view = 0; + s.min_agents_in_view = 0; + s.max_agents_in_view = 0; + s.mode_agents_in_view = 0; + s.avg_fps = 0; + s.min_fps = 0; + s.max_fps = 0; + s.mode_fps = 0; + s.a_language = ""; + s.mem_use = 0; + s.meters_traveled = 0; + s.avg_ping = 0; + s.min_ping = 0; + s.max_ping = 0; + s.mode_ping = 0; + s.regions_visited = 0; + s.run_time = 0; + s.avg_sim_fps = 0; + s.min_sim_fps = 0; + s.max_sim_fps = 0; + s.mode_sim_fps = 0; + s.start_time = 0; + s.client_version = ""; + s.s_cpu = ""; + s.s_gpu = ""; + s.s_os = ""; + s.s_ram = 0; + s.d_object_kb = 0; + s.d_texture_kb = 0; + s.d_world_kb = 0; + s.n_in_kb = 0; + s.n_in_pk = 0; + s.n_out_kb = 0; + s.n_out_pk = 0; + s.f_dropped = 0; + s.f_failed_resends = 0; + s.f_invalid = 0; + s.f_off_circuit = 0; + s.f_resent = 0; + s.f_send_packet = 0; + s._ping = new List(); + s._fps = new List(); + s._sim_fps = new List(); + s._agents_in_view = new List(); + return s; + } + } + #region structs + + public class UserSession + { + public UUID session_id; + public UUID region_id; + public string name_f; + public string name_l; + public UserSessionData session_data; + } + + public struct UserSessionData + { + public UUID session_id; + public UUID agent_id; + public UUID region_id; + public float last_updated; + public string remote_ip; + public string name_f; + public string name_l; + public float avg_agents_in_view; + public float min_agents_in_view; + public float max_agents_in_view; + public float mode_agents_in_view; + public float avg_fps; + public float min_fps; + public float max_fps; + public float mode_fps; + public string a_language; + public float mem_use; + public float meters_traveled; + public float avg_ping; + public float min_ping; + public float max_ping; + public float mode_ping; + public int regions_visited; + public float run_time; + public float avg_sim_fps; + public float min_sim_fps; + public float max_sim_fps; + public float mode_sim_fps; + public float start_time; + public string client_version; + public string s_cpu; + public string s_gpu; + public string s_os; + public int s_ram; + public float d_object_kb; + public float d_texture_kb; + public float d_world_kb; + public float n_in_kb; + public int n_in_pk; + public float n_out_kb; + public int n_out_pk; + public int f_dropped; + public int f_failed_resends; + public int f_invalid; + public int f_off_circuit; + public int f_resent; + public int f_send_packet; + public List _ping; + public List _fps; + public List _sim_fps; + public List _agents_in_view; + } + + #endregion + + public class USimStatsData + { + private UUID m_regionID = UUID.Zero; + private volatile int m_statcounter = 0; + private volatile float m_timeDilation; + private volatile float m_simFps; + private volatile float m_physicsFps; + private volatile float m_agentUpdates; + private volatile float m_rootAgents; + private volatile float m_childAgents; + private volatile float m_totalPrims; + private volatile float m_activePrims; + private volatile float m_totalFrameTime; + private volatile float m_netFrameTime; + private volatile float m_physicsFrameTime; + private volatile float m_otherFrameTime; + private volatile float m_imageFrameTime; + private volatile float m_inPacketsPerSecond; + private volatile float m_outPacketsPerSecond; + private volatile float m_unackedBytes; + private volatile float m_agentFrameTime; + private volatile float m_pendingDownloads; + private volatile float m_pendingUploads; + private volatile float m_activeScripts; + private volatile float m_scriptLinesPerSecond; + + public UUID RegionId { get { return m_regionID; } } + public int StatsCounter { get { return m_statcounter; } set { m_statcounter = value;}} + public float TimeDilation { get { return m_timeDilation; } } + public float SimFps { get { return m_simFps; } } + public float PhysicsFps { get { return m_physicsFps; } } + public float AgentUpdates { get { return m_agentUpdates; } } + public float RootAgents { get { return m_rootAgents; } } + public float ChildAgents { get { return m_childAgents; } } + public float TotalPrims { get { return m_totalPrims; } } + public float ActivePrims { get { return m_activePrims; } } + public float TotalFrameTime { get { return m_totalFrameTime; } } + public float NetFrameTime { get { return m_netFrameTime; } } + public float PhysicsFrameTime { get { return m_physicsFrameTime; } } + public float OtherFrameTime { get { return m_otherFrameTime; } } + public float ImageFrameTime { get { return m_imageFrameTime; } } + public float InPacketsPerSecond { get { return m_inPacketsPerSecond; } } + public float OutPacketsPerSecond { get { return m_outPacketsPerSecond; } } + public float UnackedBytes { get { return m_unackedBytes; } } + public float AgentFrameTime { get { return m_agentFrameTime; } } + public float PendingDownloads { get { return m_pendingDownloads; } } + public float PendingUploads { get { return m_pendingUploads; } } + public float ActiveScripts { get { return m_activeScripts; } } + public float ScriptLinesPerSecond { get { return m_scriptLinesPerSecond; } } + + public USimStatsData(UUID pRegionID) + { + m_regionID = pRegionID; + } + + public void ConsumeSimStats(SimStats stats) + { + m_regionID = stats.RegionUUID; + m_timeDilation = stats.StatsBlock[0].StatValue; + m_simFps = stats.StatsBlock[1].StatValue; + m_physicsFps = stats.StatsBlock[2].StatValue; + m_agentUpdates = stats.StatsBlock[3].StatValue; + m_rootAgents = stats.StatsBlock[4].StatValue; + m_childAgents = stats.StatsBlock[5].StatValue; + m_totalPrims = stats.StatsBlock[6].StatValue; + m_activePrims = stats.StatsBlock[7].StatValue; + m_totalFrameTime = stats.StatsBlock[8].StatValue; + m_netFrameTime = stats.StatsBlock[9].StatValue; + m_physicsFrameTime = stats.StatsBlock[10].StatValue; + m_otherFrameTime = stats.StatsBlock[11].StatValue; + m_imageFrameTime = stats.StatsBlock[12].StatValue; + m_inPacketsPerSecond = stats.StatsBlock[13].StatValue; + m_outPacketsPerSecond = stats.StatsBlock[14].StatValue; + m_unackedBytes = stats.StatsBlock[15].StatValue; + m_agentFrameTime = stats.StatsBlock[16].StatValue; + m_pendingDownloads = stats.StatsBlock[17].StatValue; + m_pendingUploads = stats.StatsBlock[18].StatValue; + m_activeScripts = stats.StatsBlock[19].StatValue; + m_scriptLinesPerSecond = stats.StatsBlock[20].StatValue; + } + } +} \ No newline at end of file -- cgit v1.1