diff --git a/Src/Witsml/Data/Tubular/WitsmlTubular.cs b/Src/Witsml/Data/Tubular/WitsmlTubular.cs index 87cbbb717..4d82a80ca 100644 --- a/Src/Witsml/Data/Tubular/WitsmlTubular.cs +++ b/Src/Witsml/Data/Tubular/WitsmlTubular.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Globalization; using System.Xml; using System.Xml.Serialization; diff --git a/Src/WitsmlExplorer.Api/Models/TubularComponent.cs b/Src/WitsmlExplorer.Api/Models/TubularComponent.cs index dce2fcb92..98ed3453d 100644 --- a/Src/WitsmlExplorer.Api/Models/TubularComponent.cs +++ b/Src/WitsmlExplorer.Api/Models/TubularComponent.cs @@ -5,10 +5,17 @@ namespace WitsmlExplorer.Api.Models public class TubularComponent { public string Uid { get; set; } + public string TypeTubularComponent { get; set; } public int? Sequence { get; set; } + public string Description { get; set; } public LengthMeasure Id { get; set; } public LengthMeasure Od { get; set; } public LengthMeasure Len { get; set; } - public string TypeTubularComponent { get; set; } + public int? NumJointStand { get; set; } + public LengthMeasure WtPerLen { get; set; } + public string ConfigCon { get; set; } + public string TypeMaterial { get; set; } + public string Vendor { get; set; } + public string Model { get; set; } } } diff --git a/Src/WitsmlExplorer.Api/Query/TubularQueries.cs b/Src/WitsmlExplorer.Api/Query/TubularQueries.cs index 7191a4f27..b7fc63a47 100644 --- a/Src/WitsmlExplorer.Api/Query/TubularQueries.cs +++ b/Src/WitsmlExplorer.Api/Query/TubularQueries.cs @@ -39,8 +39,14 @@ public static WitsmlTubulars UpdateTubularComponent(TubularComponent tubularComp WitsmlTubularComponent tc = new() { Uid = tubularComponent.Uid, + TypeTubularComp = tubularComponent.TypeTubularComponent, Sequence = tubularComponent.Sequence, - TypeTubularComp = tubularComponent.TypeTubularComponent + Description = tubularComponent.Description, + NumJointStand = tubularComponent.NumJointStand, + ConfigCon = tubularComponent.ConfigCon, + TypeMaterial = tubularComponent.TypeMaterial, + Vendor = tubularComponent.Vendor, + Model = tubularComponent.Model }; if (tubularComponent.Id != null) @@ -58,6 +64,11 @@ public static WitsmlTubulars UpdateTubularComponent(TubularComponent tubularComp tc.Len = new WitsmlLengthMeasure { Uom = tubularComponent.Len.Uom, Value = tubularComponent.Len.Value.ToString(CultureInfo.InvariantCulture) }; } + if (tubularComponent.WtPerLen != null) + { + tc.WtPerLen = new WitsmlLengthMeasure { Uom = tubularComponent.WtPerLen.Uom, Value = tubularComponent.WtPerLen.Value.ToString(CultureInfo.InvariantCulture) }; + } + return new WitsmlTubulars { Tubulars = new WitsmlTubular diff --git a/Src/WitsmlExplorer.Api/Services/TubularService.cs b/Src/WitsmlExplorer.Api/Services/TubularService.cs index 0715362c8..062f66844 100644 --- a/Src/WitsmlExplorer.Api/Services/TubularService.cs +++ b/Src/WitsmlExplorer.Api/Services/TubularService.cs @@ -49,11 +49,18 @@ public async Task> GetTubularComponents(string wel return witsmlTubular?.TubularComponents?.Select(tComponent => new TubularComponent { Uid = tComponent.Uid, + TypeTubularComponent = tComponent.TypeTubularComp, Sequence = tComponent.Sequence, + Description = tComponent.Description, Id = tComponent.Id == null ? null : new LengthMeasure { Uom = tComponent.Id.Uom, Value = decimal.Parse(tComponent.Id.Value, CultureInfo.InvariantCulture) }, Od = tComponent.Od == null ? null : new LengthMeasure { Uom = tComponent.Od.Uom, Value = decimal.Parse(tComponent.Od.Value, CultureInfo.InvariantCulture) }, Len = tComponent.Len == null ? null : new LengthMeasure { Uom = tComponent.Len.Uom, Value = decimal.Parse(tComponent.Len.Value, CultureInfo.InvariantCulture) }, - TypeTubularComponent = tComponent.TypeTubularComp, + NumJointStand = tComponent.NumJointStand, + WtPerLen = tComponent.WtPerLen == null ? null : new LengthMeasure { Uom = tComponent.WtPerLen.Uom, Value = decimal.Parse(tComponent.WtPerLen.Value, CultureInfo.InvariantCulture) }, + ConfigCon = tComponent.ConfigCon, + TypeMaterial = tComponent.TypeMaterial, + Vendor = tComponent.Vendor, + Model = tComponent.Model }).OrderBy(tComponent => tComponent.Sequence).ToList(); } diff --git a/Src/WitsmlExplorer.Api/Workers/Modify/ModifyTubularComponentWorker.cs b/Src/WitsmlExplorer.Api/Workers/Modify/ModifyTubularComponentWorker.cs index 3f4496b96..6d29e6e6a 100644 --- a/Src/WitsmlExplorer.Api/Workers/Modify/ModifyTubularComponentWorker.cs +++ b/Src/WitsmlExplorer.Api/Workers/Modify/ModifyTubularComponentWorker.cs @@ -86,6 +86,11 @@ private static void Verify(TubularComponent tubularComponent, ObjectReference tu throw new InvalidOperationException($"{nameof(tubularComponent.Sequence)} must be a positive non-zero integer"); } + if (tubularComponent.NumJointStand is not null and < 1) + { + throw new InvalidOperationException($"{nameof(tubularComponent.NumJointStand)} must be a positive non-zero integer"); + } + if (tubularComponent.Id != null && string.IsNullOrEmpty(tubularComponent.Id.Uom)) { throw new InvalidOperationException($"unit of measure for {nameof(tubularComponent.Id)} cannot be empty"); @@ -100,6 +105,11 @@ private static void Verify(TubularComponent tubularComponent, ObjectReference tu { throw new InvalidOperationException($"unit of measure for {nameof(tubularComponent.Len)} cannot be empty"); } + + if (tubularComponent.WtPerLen != null && string.IsNullOrEmpty(tubularComponent.WtPerLen.Uom)) + { + throw new InvalidOperationException($"unit of measure for {nameof(tubularComponent.WtPerLen)} cannot be empty"); + } } } } diff --git a/Src/WitsmlExplorer.Frontend/components/ContentViews/TubularView.tsx b/Src/WitsmlExplorer.Frontend/components/ContentViews/TubularView.tsx index 401546c6e..ae21785d0 100644 --- a/Src/WitsmlExplorer.Frontend/components/ContentViews/TubularView.tsx +++ b/Src/WitsmlExplorer.Frontend/components/ContentViews/TubularView.tsx @@ -83,14 +83,24 @@ export default function TubularView() { label: "typeTubularComp", type: ContentType.String }, + { property: "description", label: "description", type: ContentType.String }, { property: "innerDiameter", label: "id", type: ContentType.Measure }, { property: "od", label: "od", type: ContentType.Measure }, { property: "len", label: "len", type: ContentType.Measure }, + { property: "wtPerLen", label: "wtPerLen", type: ContentType.Measure }, { - property: "tubularName", - label: "tubular.name", + property: "numJointStand", + label: "numJointStand", + type: ContentType.Number + }, + { property: "configCon", label: "configCon", type: ContentType.String }, + { + property: "typeMaterial", + label: "typeMaterial", type: ContentType.String }, + { property: "vendor", label: "vendor", type: ContentType.String }, + { property: "model", label: "model", type: ContentType.String }, { property: "typeTubularAssy", label: "tubular.typeTubularAssy", @@ -102,8 +112,9 @@ export default function TubularView() { const tubularComponentRows = tubularComponents.map((tubularComponent) => { return { id: tubularComponent.uid, - sequence: tubularComponent.sequence, typeTubularComponent: tubularComponent.typeTubularComponent, + sequence: tubularComponent.sequence, + description: tubularComponent.description, innerDiameter: `${tubularComponent.id?.value?.toFixed(4)} ${ tubularComponent.id?.uom }`, @@ -113,7 +124,14 @@ export default function TubularView() { len: `${tubularComponent.len?.value?.toFixed(4)} ${ tubularComponent.len?.uom }`, - tubularName: tubular?.name, + numJointStand: tubularComponent.numJointStand, + wtPerLen: `${tubularComponent.wtPerLen?.value?.toFixed(4)} ${ + tubularComponent.wtPerLen?.uom + }`, + configCon: tubularComponent.configCon, + typeMaterial: tubularComponent.typeMaterial, + vendor: tubularComponent.vendor, + model: tubularComponent.model, typeTubularAssy: tubular?.typeTubularAssy, uid: tubularComponent.uid, tubularComponent: tubularComponent diff --git a/Src/WitsmlExplorer.Frontend/components/Modals/TubularComponentPropertiesModal.tsx b/Src/WitsmlExplorer.Frontend/components/Modals/TubularComponentPropertiesModal.tsx index bd0965852..f74e660e9 100644 --- a/Src/WitsmlExplorer.Frontend/components/Modals/TubularComponentPropertiesModal.tsx +++ b/Src/WitsmlExplorer.Frontend/components/Modals/TubularComponentPropertiesModal.tsx @@ -4,7 +4,10 @@ import { validText } from "components/Modals/ModalParts"; import { HideModalAction } from "contexts/operationStateReducer"; import OperationType from "contexts/operationType"; import { isInteger } from "lodash"; +import { boxPinConfigTypes } from "models/boxPinConfigTypes"; import ObjectReference from "models/jobs/objectReference"; +import { materialTypes } from "models/materialTypes"; +import MaxLength from "models/maxLength"; import { toObjectReference } from "models/objectOnWellbore"; import Tubular from "models/tubular"; import TubularComponent from "models/tubularComponent"; @@ -83,6 +86,36 @@ const TubularComponentPropertiesModal = ( "Sequence must be a positive non-zero integer" } /> + ) => + setEditableTubularComponent({ + ...editableTubularComponent, + description: e.target.value + }) + } + variant={ + tubularComponent.description && + !validText( + editableTubularComponent.description, + 1, + MaxLength.Comment + ) + ? "error" + : undefined + } + helperText={ + tubularComponent.description && + !validText( + editableTubularComponent.description, + 1, + MaxLength.Comment + ) && + `Description must be 1-${MaxLength.Comment} characters` + } + /> + ) => + setEditableTubularComponent({ + ...editableTubularComponent, + wtPerLen: { + value: parseFloat(e.target.value), + uom: editableTubularComponent.wtPerLen.uom + } + }) + } + /> + ) => + setEditableTubularComponent({ + ...editableTubularComponent, + numJointStand: parseFloat(e.target.value) + }) + } + variant={ + Number.isNaN(editableTubularComponent.numJointStand) + ? "error" + : undefined + } + helperText={ + Number.isNaN(editableTubularComponent.numJointStand) && + "numJointStand must be a positive non-zero integer" + } + /> + { + setEditableTubularComponent({ + ...editableTubularComponent, + configCon: selectedItems[0] + }); + }} + hideClearButton={!!editableTubularComponent.configCon} + /> + { + setEditableTubularComponent({ + ...editableTubularComponent, + typeMaterial: selectedItems[0] + }); + }} + hideClearButton={!!editableTubularComponent.typeMaterial} + /> + ) => + setEditableTubularComponent({ + ...editableTubularComponent, + vendor: e.target.value + }) + } + variant={ + tubularComponent.vendor && + !validText(editableTubularComponent.vendor, 1, MaxLength.Name) + ? "error" + : undefined + } + helperText={ + tubularComponent.vendor && + !validText( + editableTubularComponent.vendor, + 1, + MaxLength.Name + ) && + `Vendor must be 1-${MaxLength.Name} characters` + } + /> + ) => + setEditableTubularComponent({ + ...editableTubularComponent, + model: e.target.value + }) + } + variant={ + tubularComponent.model && + !validText(editableTubularComponent.model, 1, MaxLength.Name) + ? "error" + : undefined + } + helperText={ + tubularComponent.model && + !validText( + editableTubularComponent.model, + 1, + MaxLength.Name + ) && + `Model must be 1-${MaxLength.Name} characters` + } + /> } confirmDisabled={ !validText(editableTubularComponent.typeTubularComponent) || isInvalidSequence(editableTubularComponent.sequence) || + Number.isNaN(editableTubularComponent.numJointStand) || Number.isNaN(editableTubularComponent.id.value) || Number.isNaN(editableTubularComponent.od.value) || - Number.isNaN(editableTubularComponent.len.value) + Number.isNaN(editableTubularComponent.len.value) || + Number.isNaN(editableTubularComponent.wtPerLen.value) || + (tubularComponent.description && + !validText( + editableTubularComponent.description, + 1, + MaxLength.Comment + )) || + (tubularComponent.configCon && + !validText( + editableTubularComponent.configCon, + 1, + MaxLength.Enum + )) || + (tubularComponent.typeMaterial && + !validText( + editableTubularComponent.typeMaterial, + 1, + MaxLength.Enum + )) || + (tubularComponent.vendor && + !validText(editableTubularComponent.vendor, 1, MaxLength.Name)) || + (tubularComponent.model && + !validText(editableTubularComponent.model, 1, MaxLength.Name)) } onSubmit={() => onSubmit(editableTubularComponent)} isLoading={isLoading} diff --git a/Src/WitsmlExplorer.Frontend/models/boxPinConfigTypes.ts b/Src/WitsmlExplorer.Frontend/models/boxPinConfigTypes.ts new file mode 100644 index 000000000..4a8957601 --- /dev/null +++ b/Src/WitsmlExplorer.Frontend/models/boxPinConfigTypes.ts @@ -0,0 +1,7 @@ +export const boxPinConfigTypes = [ + "bottom box, top box", + "bottom box, top pin", + "bottom pin top box", + "bottom pin", + "unknown" +]; diff --git a/Src/WitsmlExplorer.Frontend/models/materialTypes.ts b/Src/WitsmlExplorer.Frontend/models/materialTypes.ts new file mode 100644 index 000000000..826be9e77 --- /dev/null +++ b/Src/WitsmlExplorer.Frontend/models/materialTypes.ts @@ -0,0 +1,13 @@ +export const materialTypes = [ + "aluminium", + "beryllium copper", + "chrome alloy", + "composite", + "other", + "non-magnetic steel", + "plastic", + "steel", + "steel alloy", + "titanium", + "unknown" +]; diff --git a/Src/WitsmlExplorer.Frontend/models/tubularComponent.tsx b/Src/WitsmlExplorer.Frontend/models/tubularComponent.tsx index 5a20fb502..1e935a725 100644 --- a/Src/WitsmlExplorer.Frontend/models/tubularComponent.tsx +++ b/Src/WitsmlExplorer.Frontend/models/tubularComponent.tsx @@ -1,12 +1,17 @@ import Measure from "models/measure"; export default interface TubularComponent { - sequence: number; + uid: string; typeTubularComponent: string; + sequence: number; + description: string; id: Measure; od: Measure; len: Measure; - tubularName: string; - typeTubularAssy: string; - uid: string; + numJointStand: number; + wtPerLen: Measure; + configCon: string; + typeMaterial: string; + vendor: string; + model: string; }