Overview
Start utility measurement workflows throughCloneablePlatform. Each workflow returns results asynchronously.
Prerequisites
Before starting a workflow, confirm:- The user is authenticated.
- Camera, location, and motion permissions are granted.
- Your content is wrapped in
CloneableWorkflowWrapper.
Basic setup
SwiftUI Integration
import SwiftUI
import CloneablePlatformiOS
struct UtilityMeasurementView: View {
@EnvironmentObject var cloneable: CloneablePlatform
@State private var isWorkflowActive = false
var body: some View {
// Essential: Wrap your content in CloneableWorkflowWrapper
CloneableWorkflowWrapper(cloneablePlatform: cloneable) {
VStack(spacing: 20) {
// Your UI content here
utilityWorkflowButtons
}
}
}
private var utilityWorkflowButtons: some View {
VStack(spacing: 16) {
Button("Measure Pole") {
Task { await startPoleMeasurement() }
}
.disabled(isWorkflowActive)
Button("Measure Midspan") {
Task { await startMidspanMeasurement() }
}
.disabled(isWorkflowActive)
Button("Measure Guy Wire") {
Task { await startGuyMeasurement() }
}
.disabled(isWorkflowActive)
}
}
}
Pole and vertical measurements
Basic pole measurement
func startPoleMeasurement() async {
do {
isWorkflowActive = true
// Create basic configuration
let config = VerticalMeasurementConfiguration(
type: .pole,
accuracy: .standardAccuracy,
inventoryClasses: nil,
wireClasses: nil,
guyWireOptions: nil,
ownerOptions: nil,
shouldCaptureAttachments: true
)
// Start the workflow
let result = try await cloneable.startVerticalMeasurement(config: config)
// Process the result
await processPoleMeasurementResult(result)
} catch {
print("Pole measurement failed: \(error)")
await handleMeasurementError(error)
} finally {
isWorkflowActive = false
}
}
High-accuracy pole measurement
func startHighAccuracyPoleMeasurement() async {
do {
let config = VerticalMeasurementConfiguration(
type: .pole,
accuracy: .highAccuracy, // Requires reference stick measurement
inventoryClasses: customInventoryClasses,
wireClasses: customWireClasses,
guyWireOptions: customGuyOptions,
ownerOptions: utilityOwners,
shouldCaptureAttachments: true
)
let result = try await cloneable.startVerticalMeasurement(config: config)
await processHighAccuracyResult(result)
} catch VerticalMeasurementError.referenceStickRequired {
// Handle case where high accuracy requires reference stick
await showReferenceStickInstructions()
} catch {
await handleMeasurementError(error)
}
}
Pole measurement with custom inventory
func startCustomPoleMeasurement() async {
// Define custom inventory for your utility
let customInventory = [
InventoryClass(name: "Transformer", subcategories: [
InventorySubcategory(name: "KVA Rating", options: ["25", "50", "75", "100"], required: true),
InventorySubcategory(name: "Type", options: ["Overhead", "Padmount"], required: true)
]),
InventoryClass(name: "Switch", subcategories: [
InventorySubcategory(name: "Operation", options: ["Manual", "Automatic"], required: false)
])
]
let config = VerticalMeasurementConfiguration(
type: .pole,
accuracy: .standardAccuracy,
inventoryClasses: customInventory,
wireClasses: nil,
guyWireOptions: nil,
ownerOptions: ["City Power", "Regional Coop"],
shouldCaptureAttachments: true
)
do {
let result = try await cloneable.startVerticalMeasurement(config: config)
await processCustomPoleMeasurement(result)
} catch {
await handleMeasurementError(error)
}
}
Midspan measurements
Basic midspan measurement
func startMidspanMeasurement() async {
do {
let config = VerticalMeasurementConfiguration(
type: .midspan,
accuracy: .standardAccuracy,
inventoryClasses: nil, // Midspans typically don't have attachments
wireClasses: conductorTypes, // Focus on conductor classification
guyWireOptions: nil,
ownerOptions: circuitOwners,
shouldCaptureAttachments: false // Skip attachment documentation
)
let result = try await cloneable.startVerticalMeasurement(config: config)
await processMidspanResult(result)
} catch {
print("Midspan measurement failed: \(error)")
await handleMeasurementError(error)
}
}
High-accuracy midspan with conductor types
func startHighAccuracyMidspan() async {
let conductorTypes = [
InventoryClass(name: "Primary Conductor", subcategories: [
InventorySubcategory(name: "Type", options: ["ACSR", "AAC", "AAAC"], required: true),
InventorySubcategory(name: "Size", options: ["2/0", "4/0", "336", "477"], required: true),
InventorySubcategory(name: "Voltage", options: ["12kV", "25kV", "35kV"], required: true)
])
]
let config = VerticalMeasurementConfiguration(
type: .midspan,
accuracy: .highAccuracy,
inventoryClasses: nil,
wireClasses: conductorTypes,
guyWireOptions: nil,
ownerOptions: ["Primary Utility", "Secondary Utility"],
shouldCaptureAttachments: false
)
do {
let result = try await cloneable.startVerticalMeasurement(config: config)
await processDetailedMidspanResult(result)
} catch {
await handleMeasurementError(error)
}
}
Guy anchor measurements
Basic guy wire measurement
func startGuyMeasurement() async {
do {
// Guy wire measurements use a simplified workflow
let result = try await cloneable.startGuyWorkflow()
await processGuyMeasurementResult(result)
} catch {
print("Guy wire measurement failed: \(error)")
await handleMeasurementError(error)
}
}
Guy anchor measurement with location context
func startGuyMeasurementWithContext() async {
// Ensure pole location is available for guy anchor calculations
guard let poleLocation = getCurrentPoleLocation() else {
await showLocationRequiredAlert()
return
}
do {
let result = try await cloneable.startGuyWorkflow()
// Calculate anchor location using pole location and guy measurements
let anchorLocation = calculateAnchorLocation(
poleLocation: poleLocation,
guyVector: result
)
await processGuyWithLocation(result, anchorLocation: anchorLocation)
} catch {
await handleMeasurementError(error)
}
}
Workflow state management
Manage active workflows
class WorkflowManager: ObservableObject {
@Published var isWorkflowActive = false
@Published var currentWorkflowType: WorkflowType?
enum WorkflowType {
case pole
case midspan
case guy
}
func startWorkflow(_ type: WorkflowType, with cloneable: CloneablePlatform) async {
guard !isWorkflowActive else {
print("Workflow already active")
return
}
DispatchQueue.main.async {
self.isWorkflowActive = true
self.currentWorkflowType = type
}
defer {
DispatchQueue.main.async {
self.isWorkflowActive = false
self.currentWorkflowType = nil
}
}
switch type {
case .pole:
await startPoleMeasurement(with: cloneable)
case .midspan:
await startMidspanMeasurement(with: cloneable)
case .guy:
await startGuyMeasurement(with: cloneable)
}
}
}
Result processing examples
Basic result processing
func processPoleMeasurementResult(_ result: CloneableJSON) async {
// Extract basic measurements
if let poleInfo = result["pole_information"],
let height = poleInfo["height"]?.getNumberValue() {
print("Measured pole height: \(height) feet")
}
// Extract location
if let location = result["pole_information"]?["location"]?.getLocationValue() {
print("Pole location: \(location.coordinate)")
}
// Save complete result
await saveResultToDatabase(result)
// Show success message
await showMeasurementComplete()
}
func processGuyMeasurementResult(_ result: GuyAnchorVector) async {
print("Guy wire measured:")
print(" Lead length: \(result.leadLength) feet")
print(" Angle: \(result.angle)° from north")
// Save guy data
await saveGuyDataToDatabase(result)
// Show completion
await showGuyMeasurementComplete()
}