Initial commit: establish deterministic rod-string solver stack.
Set up the C solver core, Node API orchestration, TS GUI workflow, and engineering documentation with cleaned repo hygiene for private Git hosting. Made-with: Cursor
This commit is contained in:
145
gui-ts/src/state/xmlImport.ts
Normal file
145
gui-ts/src/state/xmlImport.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import type { ParsedCase } from "../types";
|
||||
import {
|
||||
EMPTY_CASE_STATE,
|
||||
type CaseState,
|
||||
type RawFieldValue,
|
||||
type SurveyRow,
|
||||
type TaperRow
|
||||
} from "./caseModel";
|
||||
|
||||
/** Flatten xml2js node to its text content (preserves attr bag in '$' if present). */
|
||||
function textOf(value: RawFieldValue): string {
|
||||
if (value === undefined || value === null) return "";
|
||||
if (typeof value === "string") return value;
|
||||
if (typeof value === "object") {
|
||||
const obj = value as Record<string, unknown>;
|
||||
if (typeof obj._ === "string") return obj._;
|
||||
if (Object.keys(obj).length === 1 && "$" in obj) return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function numberOf(value: RawFieldValue, fallback = 0): number {
|
||||
const text = textOf(value).trim();
|
||||
if (!text) return fallback;
|
||||
const n = Number(text);
|
||||
return Number.isFinite(n) ? n : fallback;
|
||||
}
|
||||
|
||||
function stringOf(value: RawFieldValue, fallback = ""): string {
|
||||
const text = textOf(value).trim();
|
||||
return text || fallback;
|
||||
}
|
||||
|
||||
function parseColonArray(value: RawFieldValue): number[] {
|
||||
const text = textOf(value);
|
||||
if (!text) return [];
|
||||
return text
|
||||
.split(":")
|
||||
.map((piece) => piece.trim())
|
||||
.filter((piece) => piece.length > 0)
|
||||
.map((piece) => Number(piece))
|
||||
.filter((n) => Number.isFinite(n));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrate a CaseState from the `parsed` block returned by the solver-api.
|
||||
* Values come primarily from `rawFields` (pre-normalization) so the GUI
|
||||
* edits the XML-native units directly. `parsed.model` is used only as a
|
||||
* fallback when `rawFields` lacks an entry.
|
||||
*/
|
||||
export function hydrateFromParsed(parsed: ParsedCase): CaseState {
|
||||
const raw = (parsed.rawFields ?? {}) as Record<string, RawFieldValue>;
|
||||
const model = parsed.model ?? ({} as ParsedCase["model"]);
|
||||
const rawFieldOrder = Object.keys(raw);
|
||||
|
||||
const md = parseColonArray(raw.MeasuredDepthArray);
|
||||
const inc = parseColonArray(raw.InclinationFromVerticalArray);
|
||||
const azi = parseColonArray(raw.AzimuthFromNorthArray);
|
||||
const surveyLen = Math.max(md.length, inc.length, azi.length);
|
||||
const survey: SurveyRow[] = [];
|
||||
for (let i = 0; i < surveyLen; i += 1) {
|
||||
survey.push({
|
||||
md: md[i] ?? 0,
|
||||
inc: inc[i] ?? 0,
|
||||
azi: azi[i] ?? 0
|
||||
});
|
||||
}
|
||||
|
||||
const diam = parseColonArray(raw.TaperDiameterArray);
|
||||
const length = parseColonArray(raw.TaperLengthArray);
|
||||
const modulus = parseColonArray(raw.TaperModulusArray);
|
||||
const rodType = parseColonArray(raw.RodTypeArray);
|
||||
const taperLen = Math.max(diam.length, length.length, modulus.length, rodType.length);
|
||||
const taper: TaperRow[] = [];
|
||||
for (let i = 0; i < taperLen; i += 1) {
|
||||
// Stop appending "zero" rows once we've passed the meaningful entries;
|
||||
// TaperCount is the authoritative limit but we keep all rows to preserve
|
||||
// round-trip exactly.
|
||||
taper.push({
|
||||
diameter: diam[i] ?? 0,
|
||||
length: length[i] ?? 0,
|
||||
modulus: modulus[i] ?? 0,
|
||||
rodType: rodType[i] ?? 0
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
...EMPTY_CASE_STATE,
|
||||
wellName: stringOf(raw.WellName, model.wellName ?? ""),
|
||||
company: stringOf(raw.Company, model.company ?? ""),
|
||||
|
||||
pumpDepth: numberOf(raw.PumpDepth, model.pumpDepth ?? 0),
|
||||
tubingAnchorLocation: numberOf(raw.TubingAnchorLocation, model.tubingAnchorLocation ?? 0),
|
||||
tubingSize: numberOf(raw.TubingSize, model.tubingSize ?? 0),
|
||||
|
||||
pumpingSpeed: numberOf(raw.PumpingSpeed, model.pumpingSpeed ?? 0),
|
||||
pumpingSpeedOption: numberOf(raw.PumpingSpeedOption, model.pumpingSpeedOption ?? 0),
|
||||
pumpingUnitId: stringOf(raw.PumpingUnitID, model.pumpingUnitId ?? ""),
|
||||
|
||||
survey,
|
||||
taper,
|
||||
|
||||
pumpDiameter: numberOf(raw.PumpDiameter, model.pumpDiameter ?? 0),
|
||||
pumpFriction: numberOf(raw.PumpFriction, model.pumpFriction ?? 0),
|
||||
pumpIntakePressure: numberOf(raw.PumpIntakePressure, model.pumpIntakePressure ?? 0),
|
||||
pumpFillageOption: numberOf(raw.PumpFillageOption, model.pumpFillageOption ?? 0),
|
||||
percentPumpFillage: numberOf(raw.PercentPumpFillage, model.percentPumpFillage ?? 0),
|
||||
percentUpstrokeTime: numberOf(raw.PercentageUpstrokeTime, model.percentUpstrokeTime ?? 50),
|
||||
percentDownstrokeTime: numberOf(raw.PercentageDownstrokeTime, model.percentDownstrokeTime ?? 50),
|
||||
|
||||
waterCut: numberOf(raw.WaterCut, model.waterCut ?? 0),
|
||||
waterSpecGravity: numberOf(raw.WaterSpecGravity, model.waterSpecGravity ?? 1),
|
||||
fluidLevelOilGravity: numberOf(raw.FluidLevelOilGravity, model.fluidLevelOilGravity ?? 35),
|
||||
tubingGradient: numberOf(raw.TubingGradient, model.tubingGradient ?? 0),
|
||||
|
||||
rodFrictionCoefficient: numberOf(
|
||||
raw.RodFrictionCoefficient,
|
||||
model.rodFrictionCoefficient ?? 0
|
||||
),
|
||||
stuffingBoxFriction: numberOf(raw.StuffingBoxFriction, model.stuffingBoxFriction ?? 0),
|
||||
moldedGuideFrictionRatio: numberOf(
|
||||
raw.MoldedGuideFrictionRatio,
|
||||
model.moldedGuideFrictionRatio ?? 1
|
||||
),
|
||||
wheeledGuideFrictionRatio: numberOf(
|
||||
raw.WheeledGuideFrictionRatio,
|
||||
model.wheeledGuideFrictionRatio ?? 1
|
||||
),
|
||||
otherGuideFrictionRatio: numberOf(
|
||||
raw.OtherGuideFrictionRatio,
|
||||
model.otherGuideFrictionRatio ?? 1
|
||||
),
|
||||
upStrokeDamping: numberOf(raw.UpStrokeDampingFactor, model.upStrokeDamping ?? 0),
|
||||
downStrokeDamping: numberOf(raw.DownStrokeDampingFactor, model.downStrokeDamping ?? 0),
|
||||
nonDimensionalFluidDamping: numberOf(
|
||||
raw.NonDimensionalFluidDamping,
|
||||
model.nonDimensionalFluidDamping ?? 0
|
||||
),
|
||||
|
||||
unitsSelection: numberOf(raw.UnitsSelection, model.unitsSelection ?? 0),
|
||||
|
||||
rawFields: { ...raw },
|
||||
rawFieldOrder
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user