using DTS.Common.Interface.DASFactory; using DTS.Common.Utilities.Logging; using System; using System.Collections.Generic; using System.Data; using System.Linq; namespace DTS.Common.Classes.DASFactory { /// /// this class attempts to prevent delays in processing commands by controlling how many devices are talked to at once /// testing with python has shown that greatest efficiency on ATD side is by talking to one device on each port at once /// testing on DataPRO side has shown that talking to too many devices simultaneously can cause delays in device communication /// 16237 Add in ATD sequencing/staggering for services /// Add in ATD sequencing/staggering for services /// //public class ATDStagger //{ // /// // /// this maps from a S6DB to a lookup of port to a sorted list of devices. // /// this allows us to get the next device on a port of a S6DB // /// // private Dictionary>> _s6DBToPortToDevices = // new Dictionary>>(); // /// // /// any das that need to be operated on that aren't attached to a distributor // /// this makes sure we don't lose anybody in the service // /// // private List _restOfTheDAS = new List(); // public enum States // { // Waiting, //has not started // Started, //has started but not finished // Finished //has already finished // }; // /// // /// constructs a new stagger instance, with given devices [all devices enterring service] // /// and a SQL Command structure (used for querying device layout) // /// // /// // /// // public ATDStagger(IDASCommunication[] devices, IDbCommand cmd) // { // try // { // cmd.CommandType = CommandType.Text; // cmd.CommandText = "SELECT [SerialNumber], [PositionOnDistributor], [PositionOnChain], [Port], [ParentDAS] FROM [DataPro].[dbo].[DAS] WHERE [Port] > 0"; // var serialToInfo = new Dictionary>(); // var parentDASToSerials = new Dictionary>(); // //the goal here is to determine S6DB->port->position on chain mapping for all devices in the service // using (var reader = cmd.ExecuteReader()) // { // while (reader.Read()) // { // var serial = Convert.ToString(reader["SerialNumber"]); // var positionOnDistributor = Convert.ToInt32(reader["PositionOnDistributor"]); // var positionOnChain = Convert.ToInt32(reader["PositionOnChain"]); // var port = Convert.ToInt32(reader["Port"]); // var parentDAS = Convert.ToString(reader["ParentDAS"]); // var tuple = new Tuple(serial, positionOnDistributor, positionOnChain, port, parentDAS); // serialToInfo[serial] = tuple; // if (!parentDASToSerials.ContainsKey(parentDAS)) // { // parentDASToSerials[parentDAS] = new List(); // } // parentDASToSerials[parentDAS].Add(serial); // } // } // var serialsDealtWith = new HashSet(); // //tool to make it easier to look up a device given a serial number // var dasSerialToIDAS = new Dictionary(); // foreach (var das in devices) // { // dasSerialToIDAS[das.SerialNumber] = das; // } // //tool to make it easier to lookup a S6DB given an ip address // //this is used for associating S6 which aren't stored in the db // //associated to a S6DB, but are connected to one // var ipAddressToS6DB = new Dictionary(); // lock (MyLock) // { // //process every unit which is in the db attached to a S6DB // foreach (var das in devices) // { // //initialize every device enterring the service as currently waiting to start service // _deviceStates[das] = States.Waiting; // //now look for any units attached to this unit (assuming this unit is a S6DB) // if (das.IsSlice6Distributor()) // { // //store the ip address to device mapping incase there are units attached but not associated in db // var ip = ((ICommunication) das).ConnectString.Split(new[] {':'})[0]; // ipAddressToS6DB[ip] = das; // _s6DBToPortToDevices[das] = new Dictionary>(); // //S6DB normally has 4 ports, but lists them as 2,3,4,5, just for safety I'm doing 1-5 // for (int i = 0; i <= 5; i++) // { // _s6DBToPortToDevices[das][i] = new List(); // } // if (parentDASToSerials.ContainsKey(das.SerialNumber)) // { // using (var eSerials = parentDASToSerials[das.SerialNumber].GetEnumerator()) // { // while (eSerials.MoveNext()) // { // if (dasSerialToIDAS.ContainsKey(eSerials.Current) && serialToInfo.ContainsKey(eSerials.Current)) // { // var tuple = serialToInfo[eSerials.Current]; // //item 3 is the port, only ports 2-5 are really valid, but here we just check <=5 // if (tuple.Item3 <= 5) // { // _s6DBToPortToDevices[das][tuple.Item3] // .Add(dasSerialToIDAS[eSerials.Current]); // serialsDealtWith.Add(eSerials.Current); // } // } // } // } // } // } // } // //now every unit which is in the db attached to a distributor has been handled, handle any extra units // //starting with the units attached to any S6DB but not associated in the db, we'll determine those using ip addresses // foreach (var das in devices) // { // if (!serialsDealtWith.Contains(das.SerialNumber)) // { // var ip = ((ICommunication) das).ConnectString.Split(new[] {':'})[0]; // if (ipAddressToS6DB.ContainsKey(ip)) // { // var s6DB = ipAddressToS6DB[ip]; // //we don't know what port the devices is on, make it up trying to distribute evenely across ports // //even if we end up talking to 4 devices on one port it'll be better than talking to 26 devices all at once // var insertAt = 0; // var min = int.MaxValue; // for (var i = 2; i <= 5; i++) // { // if (_s6DBToPortToDevices[s6DB][i].Count < min) // { // insertAt = i; // min = _s6DBToPortToDevices[s6DB][i].Count; // } // } // _s6DBToPortToDevices[s6DB][insertAt].Add(das); // serialsDealtWith.Add(das.SerialNumber); // } // else // { // _restOfTheDAS.Add(das); // } // } // } // } // } // catch (Exception ex) // { // APILogger.Log(ex); // } // cmd.Connection.Close(); // cmd.Dispose(); // } // /// // /// returns an array of all devices which have started (but not finished) // /// this is used for checking for finished devices // /// // /// // public IDASCommunication[] GetStartedUnits() // { // lock (MyLock) // { // var any = _deviceStates.Any(device => device.Value == States.Started); // if (!any) // { // return new IDASCommunication[0]; // } // return _deviceStates.Where(device => device.Value == States.Started).Select(device => device.Key) // .ToArray(); // } // } // /// // /// returns the next device that should start the service // /// or null if no devices are ready to start // /// we want to run one device on every port on every S6DB, but ideally only 1 // /// then when one device is done we can start another // /// // /// all units in the service // /// // public IDASCommunication GetNextDevice(IDASCommunication [] units) // { // lock (MyLock) // { // using (var dasEnum = _s6DBToPortToDevices.GetEnumerator()) // { // while (dasEnum.MoveNext()) // { // var s6DB = dasEnum.Current.Key; // using (var ports = dasEnum.Current.Value.GetEnumerator()) // { // while (ports.MoveNext()) // { // var devicesOnPort = ports.Current.Value; // if (!devicesOnPort.Any()) // { // continue; // } // var started = devicesOnPort.Count(device => _deviceStates[device] == States.Started); // //if there's a started device on this port, don't send from this port // if( started >= 3){ continue; } // var anyWaiting = devicesOnPort.Any(device => _deviceStates[device] == States.Waiting && units.Contains(device)); // if( !anyWaiting ){ continue; } // var selected = devicesOnPort.First(device => _deviceStates[device] == States.Waiting && units.Contains(device)); // return selected; // } // } // if (units.Contains(s6DB) && _deviceStates[s6DB] == States.Waiting) // { // return s6DB; // } // } // } // //finally there could be devices in the service which aren't attached to any distributor (USB devices, etc) // //they have to get start too ... // var anyWaitingFromRest = _restOfTheDAS.Any(device => _deviceStates[device] == States.Waiting && units.Contains(device)); // if( !anyWaitingFromRest ){ return null; } // return _restOfTheDAS.First(device =>_deviceStates[device] == States.Waiting && units.Contains(device)); // } // } // /// // /// holds the device state (waiting/started/finished) // /// // private Dictionary _deviceStates = new Dictionary(); // /// // /// returns whether all devices are finished or not // /// // /// // public bool AreAllDevicesFinished() // { // lock (MyLock) // { // return !_deviceStates.Values.Any(state => state != States.Finished); // } // } // /// // /// lock used to control access to device states/etc // /// // private static readonly object MyLock = new object(); // /// // /// marks a devices as started // /// // /// // public void MarkDeviceStared(IDASCommunication das) // { // lock(MyLock){ _deviceStates[das] = States.Started;} // } // /// // /// marks a device as finished // /// // /// // public void MarkDeviceFinished(IDASCommunication das) // { // lock (MyLock) // { // _deviceStates[das] = States.Finished; // } // } //} }