using System.Linq; using System.Xml.Linq; using System; using System.IO; using System.Collections.Generic; using UnityEngine; //Holds the connections, the number of objects in them and the default //attenuation values to output in the absence of an object pair. public class Connection { public int name { get; set; } public int defattn { get; set; } public int maxobj { get; set; } } //Holds the object details which include the name, the type, the id and //the other objects it is linked to for each connection. public class Objects { public string name { get; set; } public string type { get; set; } public int id { get; set; } public Dictionary links = new Dictionary(); } public class TrackObject_Test : MonoBehaviour { List connectionList = new List();//List of connections of class type connection List objectlist = new List();//List of objects of class type object Dictionary[] attnupdate;//Contains an array of dictionary values of each connection type with the key being the link i.e., 1-2, 1-3, ... and the value being the final attenuation to write to file. Dictionary attnfactor = new Dictionary(); // Holds a dictionary with the key as object type and the value being the attenuation factor. Dictionary objmap = new Dictionary(); // Contains a map of the object name with its type. For example, 1 is a key for Car-121 string path = @"Attenuation.csv"; float interval, duration, frequency; float nextTime = 0; bool exec = false;//bool value to start the trackers after all objects have been loaded bool config = false;//bool value to run the trackers iff the config file exists. In its absence, the simulator exhibits normal behavior public void GetDefValues() { using (var xmlReader = new StreamReader("config.xml")) { var doc = XDocument.Load(xmlReader); XNamespace nonamespace = XNamespace.None; var xmlProducts = doc.Descendants(nonamespace + "link");//Obtains all descendants of tag 'link' foreach (var item in xmlProducts) { if (item != null && item.Attribute("default_attn") != null) { var connection = new Connection//Populates the connectionList { name = int.Parse(item.Attribute("name").Value), defattn = int.Parse(item.Attribute("default_attn").Value), maxobj = int.Parse(item.Attribute("max_obj").Value) }; connectionList.Add(connection); } } } } //Populates the objectlist from the xml file public void GetConnections() { using (var xmlReader = new StreamReader("config.xml")) { var doc = XDocument.Load(xmlReader); XNamespace nonamespace = XNamespace.None; var xmlProducts = doc.Descendants(nonamespace + "object"); foreach (var item in xmlProducts) { if (item != null && item.Attribute("id") != null) { var objlist = new Objects { name = item.Attribute("name").Value, type = item.Attribute("type").Value, id = int.Parse(item.Attribute("id").Value), }; string[] conList; foreach (var size in item.Descendants("link")) { int i = int.Parse(size.Attribute("name").Value); conList = (size.Value).Split(','); objlist.links.Add(i, conList); } objectlist.Add(objlist); } } } } //Populates the attnfactor from the config file public void GetAttnFactor() { using (var xmlReader = new StreamReader("config.xml")) { var doc = XDocument.Load(xmlReader); XNamespace nonamespace = XNamespace.None; var xmlProducts = doc.Descendants(nonamespace + "attn"); foreach (var item in xmlProducts) { if (item != null) { int fac = int.Parse(item.Attribute("factor").Value); string type = item.Attribute("type").Value; attnfactor.Add(type, fac); } } } } //Reads the simulation parameters like the time interval, run time and the frequency to be used with attenuation calculations public void GetParameters() { using (var xmlReader = new StreamReader("config.xml")) { var doc = XDocument.Load(xmlReader); XNamespace nonamespace = XNamespace.None; var xmlProducts = doc.Descendants(nonamespace + "sim_parameters"); foreach (var item in xmlProducts) { interval = int.Parse(item.Attribute("time_int").Value); duration = int.Parse(item.Attribute("max_time").Value); frequency = int.Parse(item.Attribute("frequency").Value); } } } //Populates the attenuation update dictionary with default values at time t=0. //Also allocates space to each element of Dictionary array based on the //number of elements of each connection type. public void PopulateDefaultValues() { attnupdate = new Dictionary< string, float>[connectionList.Count]; for (int i=0; i< connectionList.Count; i++) { attnupdate[i] = new Dictionary(); for (int j = 1; j <= (connectionList[i].maxobj); j++) { for (int k = j+1; k <= (connectionList[i].maxobj); k++)//n(n-1) links for max n objects { attnupdate[i].Add((j.ToString() + "-" + k.ToString()), connectionList[i].defattn); } } } } //Maps object name to "objecttype-id" void Compobjmap() { string temp = ""; for (int i = 0; i < objectlist.Count; i++) { temp = objectlist[i].type + "-" + objectlist[i].id; objmap.Add(objectlist[i].name, temp.Split('-')); } } //Computes the attenuation values for the object pairs specified in the config file. void ComputeDistance() { Vector3 coord1 = new Vector3();//Holds coordinates for the first object Vector3 coord2 = new Vector3();//Holds coordinates for the second object for (int i = 0; i < objectlist.Count; i++)//Iterate through every object { for(int j=0;j int.Parse(objectlist[i].name)) temp = objectlist[i].name + "-" + a[k]; if (temp != "" && int.Parse(a[k]) <= connectionList[j].maxobj && int.Parse(objectlist[i].name) <= connectionList[j].maxobj) attnupdate[j][temp] = DistanceFunction(Vector3.Distance(coord1, coord2)) + GetRayHit(coord1, coord2);//Update the jth index of dictionary array attn update for object i. Sums up the distance function and the attenuation caused by objects present between the source and destination. } } } } } //Writes to Attenuation.csv. Creates new file if file doesn't already exist void WriteToFile(float timestamp) { string fileText = ""; string[] output = new string[connectionList.Count]; for (int i = 0; i < connectionList.Count; i++) { output[i] = ""; for (int j = 1; j <= (connectionList[i].maxobj); j++) { for (int k = j + 1; k <= (connectionList[i].maxobj); k++) { output[i] += ((attnupdate[i][j.ToString() + "-" + k.ToString()]).ToString()) + ",";//Returns the attenuation value for the key "object 1 - object 2" } } if (i != connectionList.Count - 1) { output[i] += ","; } } fileText = timestamp.ToString() + ","; for (int i = 0; i < connectionList.Count; i++) { fileText += output[i]; } if (!File.Exists(path)) File.WriteAllText(path, fileText); else File.AppendAllText(path, fileText); File.AppendAllText(path, "\n"); } //Returns the coordinates for object type passed. //The objmap dictionary gives the name and id of the object Vector3 GetCoordinates(string item) { GameObject[] ob = null; string[] dob; objmap.TryGetValue(item, out dob); ob = GameObject.FindGameObjectsWithTag(dob[0]);//0th index is the name of the object return ob[int.Parse(dob[1])].transform.position;//1st index holds the id } //Returns the sum of the attenuation factor of all objects between 2 objects float GetRayHit(Vector3 source, Vector3 dest) { RaycastHit[] hits; string obj = ""; int fac = 0, attn = 0; hits = Physics.RaycastAll(source, dest, Vector3.Distance(source, dest));//Array of objects between source and destination for (int i = 0; i < hits.Length; i++) { obj = hits[i].collider.tag; attnfactor.TryGetValue(obj, out fac);//Fetch attnfactor for the object attn += fac; } return attn; } float DistanceFunction(float distance) { return 20f * (float)Math.Log10(distance) + 20f * (float)Math.Log10(frequency) - 27.55f ; } void Start()//Called once before start of simulation for initialization { if (File.Exists("config.xml"))//Checks if config file exists config = true; if (Time.time <= duration && config)//If config file exists and the time is less than the duration { GetDefValues();//Read XML for connections GetConnections();//Read XML for objects PopulateDefaultValues();//Populate the attnupdate dictionary with default values GetParameters();//Read XML for simulation parameters GetAttnFactor();//Read XML for attenuation factors objectlist = objectlist.OrderBy(o => o.name).ToList();//Sort objectlist by name connectionList = connectionList.OrderBy(o => o.name).ToList();//Sort connectionlist by name Compobjmap();//Maps name to object type and id } } void FixedUpdate()//Called every 0.03 secs { if (Time.time >= nextTime && Time.time <= duration && config)//Checks if its time for the next update and time is less than duration { if (exec)//Set to true if objects have been loaded. { ComputeDistance();//Compute attenuation values WriteToFile(nextTime);//Write the values to the file nextTime += (interval / 1000);//Update the clock for the next time the values have to be updated } else//See if objects have been loaded { GameObject[] sample = GameObject.FindGameObjectsWithTag("Car"); if (sample.Length > 1) { exec = true; } } } else if(Time.time > duration)//Quit if duration has exceeded { Application.Quit(); } } }