Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
063929322d |
3425
Cargo.lock
generated
Normal file
3425
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
edition = "2024"
|
||||||
|
name = "web"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
fathom-function = { git = "ssh://git@github.com/fathom-io/pipeline-calculations.git", branch = "main" }
|
||||||
|
pipeline-application = { git = "ssh://git@github.com/fathom-io/pipeline-calculations.git", branch = "main" }
|
||||||
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
|
serde-this-or-that = "0.5.0"
|
||||||
|
tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread"] }
|
||||||
|
uom = { version = "0.36" }
|
||||||
|
uuid = { version = "1" }
|
||||||
88
README.md
88
README.md
@ -1,2 +1,88 @@
|
|||||||
# 91ade006bf3341928411539994c77a8a
|
# Add field assessment entries
|
||||||
|
|
||||||
|
Field inspections carried out on sections of pipelines.
|
||||||
|
|
||||||
|
Field inspections are conducted for various reasons the two most likely are for repairs and for
|
||||||
|
validation of an ILI report.
|
||||||
|
|
||||||
|
Generally for a field inspection a approximately 15 meter long pit will be dug to expose a
|
||||||
|
complete pipeline joint along with a couple of meters of the proceeding and subsequence joints.
|
||||||
|
Multiple inspections will be carried out on the exposed joint.
|
||||||
|
|
||||||
|
## Input
|
||||||
|
|
||||||
|
### Arguments
|
||||||
|
|
||||||
|
- `org_id`: as string which should be a valid `uuid` for the organization
|
||||||
|
- `project_id`: the id of the data project where the pipeline data is found
|
||||||
|
- `pipeline_id`: an `array` of `strings` which should be valid UUIDs for pipelines
|
||||||
|
- `date`: a `string` formatted as an ISO date representing the date of the field assessment
|
||||||
|
|
||||||
|
- `log_distance`: a `float` representing the log distance of the anomaly measured in meters
|
||||||
|
- `girth_weld`: a `integer` representing the girth weld number of the joint being inspected
|
||||||
|
- `anomaly_orientation_hours`: a `integer` representing the circumferential position of the
|
||||||
|
starting point of the anomaly measured in clock position the hour part
|
||||||
|
- `anomaly_orientation_minutes`: a `integer` representing the circumferential position of the
|
||||||
|
starting point of the anomaly measured in clock position the minute part
|
||||||
|
- `measured_wall_thickness`: a `float` representing the measured wall thickness from a
|
||||||
|
location not affected by corrosion (i.e the original wall thickness).
|
||||||
|
- `measured_remaining_wall_thickness`: a `float` representing the measured wall thickness from
|
||||||
|
the river bottom of the anomaly
|
||||||
|
- `surface_location`: a `string` the surface location of the anomaly being inspected possible values
|
||||||
|
- `EXT`: external
|
||||||
|
- `INT`: internal
|
||||||
|
- `MID`: midwall
|
||||||
|
- `UNK`: unknown
|
||||||
|
- `measured_length`: a `float` representing the measured length of the anomaly
|
||||||
|
- `measured_width`: a `float` representing the measured width of the anomaly
|
||||||
|
- `measured_max_depth`: a `float` representing the measured max depth of the anomaly
|
||||||
|
- `purpose_of_measurement`: a `string` the reason for the measurement (e.g. ILI verification/Repair)
|
||||||
|
- `tool_accuracy`: a `float` representing the accuracy of the measurement tool
|
||||||
|
- `pit_gauge_accuracy`: a `float` representing the accuracy of the pit gauge tool.
|
||||||
|
- `inspector`: a `string` representing the identifier of the inspector
|
||||||
|
- `remarks`: a `string` for any additional remarks about the inspection
|
||||||
|
|
||||||
|
## Creating the function on the platform
|
||||||
|
|
||||||
|
To create this function on the platform using the `cli` set up the port forwarding as shown in README.
|
||||||
|
|
||||||
|
Then run the following command to create the function.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run functions create \
|
||||||
|
-f functions/field_assessment \
|
||||||
|
-d "Adds field assessment reports" \
|
||||||
|
-i org_id=string \
|
||||||
|
-i project_id=string \
|
||||||
|
-i pipeline_id=array \
|
||||||
|
-i date=string \
|
||||||
|
-i log_distance=float \
|
||||||
|
-i girth_weld=integer \
|
||||||
|
-i anomaly_orientation_hours=integer \
|
||||||
|
-i anomaly_orientation_minutes=integer \
|
||||||
|
-i measured_wall_thickness=float \
|
||||||
|
-i measured_remaining_wall_thickness=float \
|
||||||
|
-i surface_location=string \
|
||||||
|
-i measured_length=float \
|
||||||
|
-i measured_width=float \
|
||||||
|
-i measured_max_depth=float \
|
||||||
|
-i purpose_of_measurement=string \
|
||||||
|
-i tool_accuracy=float \
|
||||||
|
-i pit_gauge_accuracy=float \
|
||||||
|
-i inspector=string \
|
||||||
|
-i remarks=string
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing the function locally
|
||||||
|
|
||||||
|
You can run and test the function locally by running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can check it work with `curl` as follows
|
||||||
|
|
||||||
|
```bash
|
||||||
|
jq '. | tojson' functions/field_assessment/example_input.json | curl -d '@-' localhost:8080
|
||||||
|
```
|
||||||
|
|||||||
23
example_input.json
Normal file
23
example_input.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"anomaly_orientation_hours": "10",
|
||||||
|
"anomaly_orientation_minutes": "26",
|
||||||
|
"date": "Sat Sep 15 2018",
|
||||||
|
"girth_weld": 820,
|
||||||
|
"inspector": "jbell",
|
||||||
|
"log_distance": 1105,
|
||||||
|
"measured_length": 120,
|
||||||
|
"measured_max_depth": 1.06,
|
||||||
|
"measured_remaining_wall_thickness": null,
|
||||||
|
"measured_wall_thickness": null,
|
||||||
|
"measured_width": null,
|
||||||
|
"org_id": "2cbfe270-d195-48ad-aed1-24145924635c",
|
||||||
|
"pipeline_id": [
|
||||||
|
"01966d47-1d4c-7751-a1f1-0617caa3a00d"
|
||||||
|
],
|
||||||
|
"pit_gauge_accuracy": null,
|
||||||
|
"project_id": "680b61b0aedd6f9e639d8699",
|
||||||
|
"purpose_of_measurement": "ili_verification",
|
||||||
|
"remarks": null,
|
||||||
|
"surface_location": "EXT",
|
||||||
|
"tool_accuracy": null
|
||||||
|
}
|
||||||
163
src/main.rs
Normal file
163
src/main.rs
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
use fathom_function::{chrono::NaiveDate, forms::deserialize_date, tracing};
|
||||||
|
use pipeline_application::{
|
||||||
|
application::{
|
||||||
|
Application, FieldAssessment, PurposeOfMeasurement, SurfaceLocation,
|
||||||
|
features::{FeatureIdentification, FeatureType},
|
||||||
|
orientation::Orientation,
|
||||||
|
},
|
||||||
|
serialization::{serialize_meter, serialize_millimeter},
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_this_or_that::as_opt_u64;
|
||||||
|
use uom::si::f64::Length;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[fathom_function::function]
|
||||||
|
async fn upload_field_assessment(input: Input) -> Result<Output, String> {
|
||||||
|
let app = Application::new_from_compile_env(input.org_id, input.project_id).unwrap();
|
||||||
|
|
||||||
|
for pipeline_id in input.pipeline_id {
|
||||||
|
app.upload_field_assessment(pipeline_id, &input.report)
|
||||||
|
.await
|
||||||
|
.map_err(|err| {
|
||||||
|
tracing::error!(%pipeline_id, ?err, "Error uploading field assessment");
|
||||||
|
format!("{err:?}")
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Output {
|
||||||
|
status: "Success".to_owned(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
struct Output {
|
||||||
|
status: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct Input {
|
||||||
|
org_id: Uuid,
|
||||||
|
project_id: String,
|
||||||
|
pipeline_id: Vec<Uuid>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
report: Report,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct Report {
|
||||||
|
/// The date of this field inspection.
|
||||||
|
#[serde(deserialize_with = "deserialize_date")]
|
||||||
|
date: NaiveDate,
|
||||||
|
/// The log distance: the absolute distance measured in meters from the launcher.
|
||||||
|
#[serde(with = "serialize_meter")]
|
||||||
|
log_distance: Length,
|
||||||
|
|
||||||
|
/// The Girthweld number. These follow a sequential order of 10 (e.g., 10, 20, 30, ...) to
|
||||||
|
/// enumerate each joint from the initial to the final one. This ordering system facilitates
|
||||||
|
/// future repairs using the "Pipeline Sectional Replacement" method.
|
||||||
|
#[serde(deserialize_with = "as_opt_u64")]
|
||||||
|
girth_weld: Option<u64>,
|
||||||
|
|
||||||
|
/// The circumferential position of the starting point of an anomaly, specifically the top-left
|
||||||
|
/// position on the pipe.
|
||||||
|
///
|
||||||
|
/// This orientation is measured in O'clock notation, ranging from 00:00 to 12:00, indicating
|
||||||
|
/// the position around the circumference of the pipe. This is the number of hours
|
||||||
|
#[serde(deserialize_with = "as_opt_u64")]
|
||||||
|
anomaly_orientation_hours: Option<u64>,
|
||||||
|
|
||||||
|
/// The circumferential position of the starting point of an anomaly, specifically the top-left
|
||||||
|
/// position on the pipe.
|
||||||
|
///
|
||||||
|
/// This orientation is measured in O'clock notation, ranging from 00:00 to 12:00, indicating
|
||||||
|
/// the position around the circumference of the pipe. This is the number of minutes
|
||||||
|
#[serde(deserialize_with = "as_opt_u64")]
|
||||||
|
anomaly_orientation_minutes: Option<u64>,
|
||||||
|
|
||||||
|
/// The measured wall thickness of the pipeline taken from an un-corroded location. Corresponds
|
||||||
|
/// to the un-corroded wall thickness.
|
||||||
|
#[serde(with = "serialize_millimeter::opt")]
|
||||||
|
measured_wall_thickness: Option<Length>,
|
||||||
|
|
||||||
|
/// The measured wall thickness of the pipeline taken from the river bottom of the corrosion.
|
||||||
|
#[serde(with = "serialize_millimeter::opt")]
|
||||||
|
measured_remaining_wall_thickness: Option<Length>,
|
||||||
|
|
||||||
|
/// The surface location of the anomaly.
|
||||||
|
///
|
||||||
|
/// Each identified anomaly, denoted as ANOM, falls into one of four categories: Internal
|
||||||
|
/// (INT), External (EXT), Midwall (MID), or Unknown (UNK) for any reason. These
|
||||||
|
/// classifications are assigned based on the specific characteristics and location of the
|
||||||
|
/// anomaly on the pipeline. The categorization helps provide a comprehensive understanding of
|
||||||
|
/// the nature of the anomalies present.
|
||||||
|
surface_location: Option<SurfaceLocation>,
|
||||||
|
|
||||||
|
/// The measured length (in mm) of an anomaly, (the length of the corrosion box) in relation to
|
||||||
|
/// the reporting threshold.
|
||||||
|
#[serde(with = "serialize_millimeter")]
|
||||||
|
measured_length: Length,
|
||||||
|
|
||||||
|
/// The circumferential width of an anomaly, this denotes the measured measurement of the
|
||||||
|
/// anomaly's width in circumferential direction, comparable to the width of the corrosion box,
|
||||||
|
/// concerning the reporting threshold and the unit is mm.
|
||||||
|
#[serde(with = "serialize_millimeter::opt")]
|
||||||
|
measured_width: Option<Length>,
|
||||||
|
|
||||||
|
/// The measured depth of the anomaly.
|
||||||
|
///
|
||||||
|
/// This measurement is essential for evaluating the structural integrity of the pipeline. A
|
||||||
|
/// higher percentage of metal loss indicates a more significant impact on the pipeline's
|
||||||
|
/// strength and durability.
|
||||||
|
#[serde(with = "serialize_millimeter")]
|
||||||
|
measured_max_depth: Length,
|
||||||
|
|
||||||
|
/// The reason for this field assessment
|
||||||
|
purpose_of_measurement: PurposeOfMeasurement,
|
||||||
|
|
||||||
|
/// The accuracy of the tool used
|
||||||
|
#[serde(with = "serialize_millimeter::opt")]
|
||||||
|
tool_accuracy: Option<Length>,
|
||||||
|
|
||||||
|
/// The accuracy of the pit gauge used
|
||||||
|
#[serde(with = "serialize_millimeter::opt")]
|
||||||
|
pit_gauge_accuracy: Option<Length>,
|
||||||
|
|
||||||
|
/// The inspector name or identifier
|
||||||
|
inspector: String,
|
||||||
|
/// And additional remarks about the inspection
|
||||||
|
remarks: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Report {
|
||||||
|
fn orientation(&self) -> Option<Orientation> {
|
||||||
|
Some(Orientation::new(
|
||||||
|
self.anomaly_orientation_hours? as _,
|
||||||
|
self.anomaly_orientation_minutes? as _,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Report> for FieldAssessment {
|
||||||
|
fn from(value: &Report) -> Self {
|
||||||
|
Self {
|
||||||
|
date: value.date.and_time(Default::default()).and_utc(),
|
||||||
|
log_distance: value.log_distance,
|
||||||
|
feature_type: Some(FeatureType::ANOM),
|
||||||
|
feature_identification: Some(FeatureIdentification::CORR),
|
||||||
|
girth_weld: value.girth_weld.map(|v| v as _),
|
||||||
|
anomaly_orientation: value.orientation(),
|
||||||
|
measured_wall_thickness: value.measured_wall_thickness,
|
||||||
|
measured_remaining_wall_thickness: value.measured_remaining_wall_thickness,
|
||||||
|
surface_location: value.surface_location,
|
||||||
|
measured_length: value.measured_length,
|
||||||
|
measured_width: value.measured_width,
|
||||||
|
measured_max_depth: value.measured_max_depth,
|
||||||
|
purpose_of_measurement: value.purpose_of_measurement.to_owned(),
|
||||||
|
tool_accuracy: value.tool_accuracy,
|
||||||
|
pit_gauge_accuracy: value.pit_gauge_accuracy,
|
||||||
|
inspector: value.inspector.to_owned(),
|
||||||
|
remarks: value.remarks.to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user