/// <summary> /// Generates a splice closure at the coincident endpoint. Does not start an edit operation. /// </summary> /// <param name="cableA">Cable A</param> /// <param name="cableB">Cable B</param> /// <returns>SpliceClosureWRapper based on new feature</returns> private SpliceClosureWrapper GenerateSpliceClosure(FiberCableWrapper cableA, SpliceableCableWrapper cableB) { SpliceClosureWrapper wrapper = null; #region Validation if (null == cableA) { throw new ArgumentNullException("cableA"); } if (null == cableB) { throw new ArgumentNullException("cableB"); } if (ESRI.ArcGIS.Editor.esriEditState.esriStateNotEditing == _editor.EditState) { throw new InvalidOperationException("You must be editing to perform this operation"); } #endregion // Populate an IPID; we will need it Guid g = Guid.NewGuid(); string spliceIpid = g.ToString("B").ToUpper(); try { ESRI.ArcGIS.Geometry.IPolyline line = cableB.Feature.Shape as ESRI.ArcGIS.Geometry.IPolyline; if (null != line) { ESRI.ArcGIS.Geometry.IPoint splicePoint = null; if (cableB.IsThisFromEnd) { splicePoint = line.FromPoint; } else { splicePoint = line.ToPoint; } ESRI.ArcGIS.Geodatabase.IFeatureClass spliceClosureFtClass = _wkspHelper.FindFeatureClass(ConfigUtil.SpliceClosureFtClassName); // ESRI.ArcGIS.Geodatabase.IFeatureClass spliceClosureFtClass = GdbUtils.GetFeatureClass(cableA.Feature.Class, ConfigUtil.SpliceClosureFtClassName); ESRI.ArcGIS.Geodatabase.IFeature spliceFt = spliceClosureFtClass.CreateFeature(); spliceFt.Shape = splicePoint; spliceFt.set_Value(spliceClosureFtClass.Fields.FindField(ConfigUtil.IpidFieldName), spliceIpid); spliceFt.Store(); wrapper = new SpliceClosureWrapper(spliceFt); } } catch { wrapper = null; } return wrapper; }
/// <summary> /// Splices all available strands from one cable to the other, at a given splice closure /// </summary> /// <param name="cableA">Cable A</param> /// <param name="cableB">Cable B</param> /// <param name="splice">Splice Closure</param> /// <param name="strands">Strands to splice together</param> /// <param name="isExistingOperation">Flag to control whether we need to wrap this in an edit operation</param> /// <returns>Success</returns> public bool CreateSplices(FiberCableWrapper cableA, SpliceableCableWrapper cableB, SpliceClosureWrapper splice, Dictionary<int,FiberSplice> strands, bool isExistingOperation) { bool success = false; bool isOperationOpen = false; #region Validation if (null == cableA) { throw new ArgumentNullException("cableA"); } if (null == cableB) { throw new ArgumentNullException("cableB"); } if (ESRI.ArcGIS.Editor.esriEditState.esriStateNotEditing == _editor.EditState) { throw new InvalidOperationException("You must be editing to perform this operation"); } #endregion if (!isExistingOperation) { _editor.StartOperation(); isOperationOpen = true; } if (null == splice) { splice = GenerateSpliceClosure(cableA, cableB); } else if (0 == splice.IPID.Length) { // Populate an IPID; we will need it ESRI.ArcGIS.Geodatabase.IFeature spliceFt = splice.Feature; Guid g = Guid.NewGuid(); spliceFt.set_Value(spliceFt.Fields.FindField(ConfigUtil.IpidFieldName), g.ToString("B").ToUpper()); spliceFt.Store(); } try { ESRI.ArcGIS.Geodatabase.ITable fiberSpliceTable = _wkspHelper.FindTable(ConfigUtil.FiberSpliceTableName); // ESRI.ArcGIS.Geodatabase.ITable fiberSpliceTable = GdbUtils.GetTable(cableA.Feature.Class, ConfigUtil.FiberSpliceTableName); int aCableIdx = fiberSpliceTable.FindField(ConfigUtil.ACableIdFieldName); int bCableIdx = fiberSpliceTable.FindField(ConfigUtil.BCableIdFieldName); int aFiberNumIdx = fiberSpliceTable.FindField(ConfigUtil.AFiberNumberFieldName); int bFiberNumIdx = fiberSpliceTable.FindField(ConfigUtil.BFiberNumberFieldName); int spliceIpidIdx = fiberSpliceTable.FindField(ConfigUtil.SpliceClosureIpidFieldName); int isAFromIdx = fiberSpliceTable.FindField(ConfigUtil.IsAFromEndFieldName); int isBFromIdx = fiberSpliceTable.FindField(ConfigUtil.IsBFromEndFieldName); int lossIdx = fiberSpliceTable.FindField(ConfigUtil.LossFieldName); int typeIdx = fiberSpliceTable.FindField(ConfigUtil.TypeFieldName); ESRI.ArcGIS.Geodatabase.IField typeField = fiberSpliceTable.Fields.get_Field(typeIdx); string aCableId = cableA.IPID; string bCableId = cableB.IPID; string isAFromEnd = cableB.IsOtherFromEnd ? "T" : "F"; string isBFromEnd = cableB.IsThisFromEnd ? "T" : "F"; string spliceIpid = splice.IPID; using (ESRI.ArcGIS.ADF.ComReleaser releaser = new ESRI.ArcGIS.ADF.ComReleaser()) { // TODO cant use insert cursor since edit events wont fire. // We need evetns to fire for dynamic values to get populated. // ESRI.ArcGIS.Geodatabase.ICursor insertCursor = fiberSpliceTable.Insert(true); // releaser.ManageLifetime(insertCursor); foreach (KeyValuePair<int, FiberSplice> pair in strands) { IRow row = fiberSpliceTable.CreateRow(); releaser.ManageLifetime(row); FiberSplice fiberSplice = pair.Value; row.set_Value(aCableIdx, aCableId); row.set_Value(bCableIdx, bCableId); row.set_Value(aFiberNumIdx, pair.Key); row.set_Value(bFiberNumIdx, fiberSplice.BRange.Low); row.set_Value(spliceIpidIdx, spliceIpid); row.set_Value(isAFromIdx, isAFromEnd); row.set_Value(isBFromIdx, isBFromEnd); if (null == fiberSplice.Loss) { row.set_Value(lossIdx, DBNull.Value); } else { row.set_Value(lossIdx, fiberSplice.Loss); } object typeValue = DBNull.Value; if (null != fiberSplice.Type) { try { typeValue = GdbUtils.CheckForCodedValue(typeField, fiberSplice.Type); } catch { // TODO: Log a warning about why we can't set the default split splice type? } } row.set_Value(typeIdx, typeValue); row.Store(); } } if (isOperationOpen) { _editor.StopOperation("Edit Splices"); } success = true; } catch { if (isOperationOpen) { _editor.AbortOperation(); } success = false; } return success; }
/// <summary> /// Deletes given splices from one cable to the other, at a given splice closure /// </summary> /// <param name="cableA">Cable A</param> /// <param name="cableB">Cable B</param> /// <param name="splice">Splice Closure</param> /// <param name="strands">Strands to remove</param> /// <param name="isExistingOperation">Flag to control whether we need to wrap this in an edit operation</param> /// <returns>Success</returns> public bool BreakSplices(FiberCableWrapper cableA, SpliceableCableWrapper cableB, SpliceClosureWrapper splice, Dictionary<int,FiberSplice> strands, bool isExistingOperation) { bool success = false; bool isOperationOpen = false; #region Validation if (null == cableA) { throw new ArgumentNullException("cableA"); } if (null == cableB) { throw new ArgumentNullException("cableB"); } if (null == splice) { throw new ArgumentNullException("splice"); } if (ESRI.ArcGIS.Editor.esriEditState.esriStateNotEditing == _editor.EditState) { throw new InvalidOperationException("You must be editing to perform this operation"); } #endregion if (!isExistingOperation) { _editor.StartOperation(); isOperationOpen = true; } try { ESRI.ArcGIS.Geodatabase.ITable spliceTable = _wkspHelper.FindTable(ConfigUtil.FiberSpliceTableName); // ESRI.ArcGIS.Geodatabase.ITable spliceTable = GdbUtils.GetTable(splice.Feature.Class, ConfigUtil.FiberSpliceTableName); using (ESRI.ArcGIS.ADF.ComReleaser releaser = new ESRI.ArcGIS.ADF.ComReleaser()) { ESRI.ArcGIS.Geodatabase.IQueryFilter filter = new ESRI.ArcGIS.Geodatabase.QueryFilterClass(); releaser.ManageLifetime(filter); // A and B is arbitrary, so we check the given combinations going both ways. The structure is: // Where the splice closure IPID is ours and // ((the A Cable/Fiber matches our A cable and B Cable/Fiber matches our B) // OR (the A Cable/Fiber matches our B cable and B Cable/Fiber matches our A)) string format = "{0}='{1}' AND (({2}='{3}' AND {4}={5} AND {6}='{7}' AND {8}={9})" + " OR ({2}='{7}' AND {4}={9} AND {6}='{3}' AND {8}={5}))"; foreach (KeyValuePair<int, FiberSplice> pair in strands) { filter.WhereClause = string.Format(format, ConfigUtil.SpliceClosureIpidFieldName, splice.IPID, ConfigUtil.ACableIdFieldName, cableA.IPID, ConfigUtil.AFiberNumberFieldName, pair.Key, ConfigUtil.BCableIdFieldName, cableB.IPID, ConfigUtil.BFiberNumberFieldName, pair.Value.BRange.Low); spliceTable.DeleteSearchedRows(filter); } if (isOperationOpen) { _editor.StopOperation("Break Splices"); isOperationOpen = false; } success = true; } } catch { if (isOperationOpen) { _editor.AbortOperation(); } success = false; } return success; }
/// <summary> /// Check changes to the grid and save them to the database /// </summary> /// <param name="splice">The associated splice closure</param> /// <param name="cableA">A cable</param> /// <param name="cableB">The other cable</param> /// <returns>Success</returns> private bool SaveChanges(SpliceClosureWrapper splice, FiberCableWrapper cableA, SpliceableCableWrapper cableB) { bool result = false; string isNotOkString = string.Empty; Dictionary<int, FiberSplice> currentGrid = new Dictionary<int, FiberSplice>(); List<int> currentBStrands = new List<int>(); try { int aIdx = colRangeA.Index; int bIdx = colRangeB.Index; int lossIdx = colLoss.Index; int typeIdx = grdSplices.Columns[colType.Name].Index; // If we had to use colTypeText, it will be using the same name // Less than count-1 lets us avoid the insert row for (int i = 0; i < grdSplices.Rows.Count - 1; i++) { if (grdSplices[aIdx, i].Value == null || grdSplices[bIdx, i].Value == null) { isNotOkString = "A or B unit range missing."; } if (0 < isNotOkString.Length) { // No need to check the rest if this one was not OK break; } List<Range> aRanges = SpliceAndConnectionUtils.ParseRanges(grdSplices[aIdx, i].Value.ToString()); List<Range> bRanges = SpliceAndConnectionUtils.ParseRanges(grdSplices[bIdx, i].Value.ToString()); if (!SpliceAndConnectionUtils.AreCountsEqual(aRanges, bRanges)) { isNotOkString = "Number of units from A to B must match on each row."; } else if (!SpliceAndConnectionUtils.AreRangesWithinFiberCount(aRanges, cableA)) { isNotOkString = "Selected units exceed fiber count for cable A."; } else if (!SpliceAndConnectionUtils.AreRangesWithinFiberCount(bRanges, cableB)) { isNotOkString = "Selected units exceed fiber count for cable B."; } if (0 < isNotOkString.Length) { // No need to check the rest if this one was not OK break; } List<Connection> matchedUp = SpliceAndConnectionUtils.MatchUp(aRanges, bRanges); foreach (Connection range in matchedUp) { Range a = range.ARange; Range b = range.BRange; int numUnits = a.High - a.Low + 1; for (int offset = 0; offset < numUnits; offset++) { int aUnit = a.Low + offset; int bUnit = b.Low + offset; if (currentGrid.ContainsKey(aUnit)) { isNotOkString = string.Format("Duplicate splicing found for A Strand {0}", aUnit); // No need to check the rest if this one was not OK break; } else if (currentBStrands.Contains(bUnit)) { isNotOkString = string.Format("Duplicate splicing found for B Strand {0}", bUnit); // No need to check the rest if this one was not OK break; } else { object lossObj = grdSplices[lossIdx, i].Value; object typeObj = grdSplices[typeIdx, i].Value; double? loss = null; if (null != lossObj) { double dblLoss = -1; if (double.TryParse(lossObj.ToString(), out dblLoss)) { loss = dblLoss; } else { MessageBox.Show("Loss value on row {0} could not be parsed. Using null.", "Splice Editor", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } FiberSplice fiberSplice = new FiberSplice(new Range(aUnit, aUnit), new Range(bUnit, bUnit), loss, typeObj); currentGrid[aUnit] = fiberSplice; currentBStrands.Add(bUnit); } } } } } catch (Exception ex) { isNotOkString = ex.Message; } // Check the ranges are within the feature's units List<int> checkToUnits = new List<int>(); List<int> checkFromUnits = new List<int>(); // Anything that is in the current grid, we will see if it is available. But if it was deleted, we can ignore // checking its availabilty, because we are about to free it up. Also if it was original, we can ignore it, // since we are reprocessing it. Duplicates ON the grid have already been checked for. // NOTE: We can simplify this to just check original, since any deleted ones were in the original. foreach (FiberSplice checkSplice in currentGrid.Values) { int unit = checkSplice.BRange.Low; checkToUnits.Add(unit); } foreach (FiberSplice checkSplice in _original.Values) { int unit = checkSplice.BRange.Low; if (checkToUnits.Contains(unit)) { checkToUnits.Remove(unit); } } foreach (int fromUnit in currentGrid.Keys) { if (!_original.ContainsKey(fromUnit)) { checkFromUnits.Add(fromUnit); } } if (!SpliceAndConnectionUtils.AreRangesAvailable(checkFromUnits, cableA, cableB.IsOtherFromEnd)) { isNotOkString = "Some A units are not in the available ranges for the A Cable."; } else if (!SpliceAndConnectionUtils.AreRangesAvailable(checkToUnits, cableB, cableB.IsThisFromEnd)) { isNotOkString = "Some B units are not in the available ranges for the B Cable."; } if (0 < isNotOkString.Length) { string message = string.Format("{0}\nPlease correct this and try again.", isNotOkString); MessageBox.Show(message, "Splice Editor", MessageBoxButtons.OK, MessageBoxIcon.Error); } else { if (null != cableA && null != cableB) { // For the deleted ones, if they were added back, don't delete them... List<int> keys = new List<int>(); keys.AddRange(_deleted.Keys); foreach (int key in keys) { if (currentGrid.ContainsKey(key)) { FiberSplice fiberSplice = currentGrid[key]; if (fiberSplice.BRange.Low == _deleted[key].BRange.Low && fiberSplice.Loss == _deleted[key].Loss && fiberSplice.Type == _deleted[key].Type) { // It is added back, so don't delete it _deleted.Remove(key); } } } if (0 < _deleted.Count) { _spliceHelper.BreakSplices(cableA, cableB, splice, _deleted, false); } // For the added ones, if they already exist or are not available, don't add them // Since we already know they are in the fiber count range, the only problem would be if they were already // spliced. This would be the case if (1) it was part of the original or (2) has already appeared higher // on the currentGrid. (2) is handled when building currentGrid, by checking if the aUnit or bUnit was already used. keys.Clear(); keys.AddRange(currentGrid.Keys); foreach (int key in keys) { if (_original.ContainsKey(key)) { FiberSplice fiberSplice = currentGrid[key]; if (fiberSplice.BRange.Low == _original[key].BRange.Low && fiberSplice.Loss == _original[key].Loss && fiberSplice.Type == _original[key].Type) { // It was on the original, so we don't need to create it currentGrid.Remove(key); } } } if (0 < currentGrid.Count) { _spliceHelper.CreateSplices(cableA, cableB, splice, currentGrid, false); } // These are no longer part of the originals foreach (KeyValuePair<int, FiberSplice> deletedPair in _deleted) { _original.Remove(deletedPair.Key); } // These are now part of the originals foreach (KeyValuePair<int, FiberSplice> addedPair in currentGrid) { _original[addedPair.Key] = addedPair.Value; } _deleted.Clear(); // The grid is fresh // Set the existing rows as committed data. Less than count-1 lets us avoid the insert row for (int i = 0; i < grdSplices.Rows.Count - 1; i++) { grdSplices.Rows[i].ReadOnly = true; } btnSave.Enabled = false; btnSave.Tag = false; // No edits made yet result = true; } } return result; }
/// <summary> /// Loads the grid with existing splice ranges between the given features /// </summary> /// <param name="spliceWrapper">Selected Splice Closure</param> /// <param name="cableA">Selected Cable A</param> /// <param name="cableB">Selected Cable B</param> private void LoadExistingRecords(SpliceClosureWrapper spliceWrapper, FiberCableWrapper cableA, SpliceableCableWrapper cableB) { ClearGrid(); if (null != spliceWrapper && null != cableA && null != cableB) { List<FiberSplice> rangesAtoB = SpliceAndConnectionUtils.GetSplicedRanges(cableA, cableB, spliceWrapper); for (int i = 0; i < rangesAtoB.Count; i++) { FiberSplice fiberSplice = rangesAtoB[i]; int rowIdx = grdSplices.Rows.Add(fiberSplice.ARange, fiberSplice.BRange, fiberSplice.Loss, fiberSplice.Type); grdSplices.Rows[rowIdx].ReadOnly = true; // Allow this to be deleted, but not edited. Range a = fiberSplice.ARange; Range b = fiberSplice.BRange; int numUnits = a.High - a.Low + 1; for (int offset = 0; offset < numUnits; offset++) { int aUnit = a.Low + offset; int bUnit = b.Low + offset; FiberSplice originalSplice = new FiberSplice(new Range(aUnit, aUnit), new Range(bUnit, bUnit), fiberSplice.Loss, fiberSplice.Type); _original[aUnit] = originalSplice; } } // These are valid for display because it is these two cables at this splice, the A and B side assignment is // arbitrary. We do need to reverse them in the display though. For example is Cable X (1-12) is spliced to // Cable Y (36-47), and they have selected X/Y in the form -- the grid should show X's units on the left and // Y's on the right. Here we are requesting them as Y/X though. List<FiberSplice> rangesBtoA = SpliceAndConnectionUtils.GetSplicedRanges((FiberCableWrapper)cboCableB.SelectedItem, (FiberCableWrapper)cboCableA.SelectedItem, spliceWrapper); for (int i = 0; i < rangesBtoA.Count; i++) { FiberSplice fiberSplice = rangesBtoA[i]; int rowIdx = grdSplices.Rows.Add(fiberSplice.BRange, fiberSplice.ARange, fiberSplice.Loss, fiberSplice.Type); grdSplices.Rows[rowIdx].ReadOnly = true; // Allow this to be deleted, but not edited. Range a = fiberSplice.ARange; Range b = fiberSplice.BRange; int numUnits = a.High - a.Low + 1; for (int offset = 0; offset < numUnits; offset++) { int aUnit = a.Low + offset; int bUnit = b.Low + offset; FiberSplice originalSplice = new FiberSplice(new Range(bUnit, bUnit), new Range(aUnit, aUnit), fiberSplice.Loss, fiberSplice.Type); _original[bUnit] = originalSplice; } } } btnSave.Enabled = false; btnSave.Tag = false; // No edits made yet }
/// <summary> /// Prompts the user about saving pending edits /// </summary> /// <param name="spliceWrapper"></param> /// <param name="cableAWrapper"></param> /// <param name="cableBWrapper"></param> /// <returns>False if the user chooses to cancel what they were doing; True if they choose Yes or No /// (which means they are OK with what they are doing)</returns> private bool IsUserSure(SpliceClosureWrapper spliceWrapper, FiberCableWrapper cableAWrapper, SpliceableCableWrapper cableBWrapper) { bool result = true; // Assume they do not want to save and do want to continue DialogResult dialogResult = DialogResult.No; dialogResult = MessageBox.Show("You have unsaved edits. Would you like to save them before closing?", "Connection Editor", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question); if (DialogResult.Cancel == dialogResult) { // The user isn't sure about what they are doing and wants to cancel it result = false; } else if (DialogResult.Yes == dialogResult) { bool isSaveOk = SaveChanges(spliceWrapper, cableAWrapper, cableBWrapper); if (!isSaveOk) { // They wanted to save but it didn't work. They probably got a message telling them what to fix. Cancel // whatever they were doing so that they have a chance to do so result = false; } } return result; }