from dataclasses import dataclass REQUIRED_SIMPLIFIED_FIELD_RULES = { "ts": (1, None), "deptbitm": (0.0, 20000.0), "chkp": (0.0, 20000.0), "sppa": (0.0, 20000.0), "rpma": (0, 400), "torqa": (0.0, 100000.0), "hkla": (0.0, 2000.0), "blkpos": (0.0, 1000.0), "woba": (0.0, 2000.0), } REQUIRED_TRANSMISSION_CHANNELS = { "0108": "deptbitm", "0112": "blkpos", "0114": "hkla", "0116": "woba", "0118": "torqa", "0120": "rpma", "0121": "sppa", "0122": "chkp", } def validate_required_wits_fields(data): for field_name, (minimum, maximum) in REQUIRED_SIMPLIFIED_FIELD_RULES.items(): value = getattr(data, field_name) if value is None: raise ValueError(f"WITS field '{field_name}' is required") if value < minimum: raise ValueError(f"WITS field '{field_name}' must be >= {minimum}, got {value}") if maximum is not None and value > maximum: raise ValueError(f"WITS field '{field_name}' must be <= {maximum}, got {value}") @dataclass(frozen=True) class WitsData: ts: int wellid: str stknum: int recid: int seqid: int actual_date: int actual_time: int actual_ts: int actcod: int deptbitm: float deptbitv: float deptmeas: float deptvert: float blkpos: float ropa: float hkla: float hklx: float woba: float wobx: float torqa: float torqx: float rpma: int sppa: float chkp: float spm1: int spm2: int spm3: int tvolact: float tvolcact: float mfop: int mfoa: float mfia: float mdoa: float mdia: float mtoa: float mtia: float mcoa: float mcia: float stkc: int lagstks: int deptretm: float gasa: float space1: float space2: float space3: float space4: float space5: float def __post_init__(self): validate_required_wits_fields(self) def to_string(self) -> str: parts = [] for channel, field_name, field_type in WITS_CHANNEL_MAPPING: value = getattr(self, field_name, None) if value is None: continue if field_type == "string": formatted = str(value) elif field_type == "int": formatted = str(int(value)) elif field_type == "float6": formatted = f"{float(value):.6f}" else: formatted = str(value) parts.append(f"{channel}{formatted}") return "".join(parts) WITS_CHANNEL_MAPPING = [ ("0101", "wellid", "string"), ("0102", "stknum", "int"), ("0103", "recid", "int"), ("0104", "seqid", "int"), ("0105", "actual_date", "int"), ("0106", "actual_time", "int"), ("0107", "actcod", "int"), ("0108", "deptbitm", "float6"), ("0109", "deptbitv", "float6"), ("0110", "deptmeas", "float6"), ("0111", "deptvert", "float6"), ("0112", "blkpos", "float6"), ("0113", "ropa", "float6"), ("0114", "hkla", "float6"), ("0116", "woba", "float6"), ("0117", "wobx", "float6"), ("0118", "torqa", "float6"), ("0119", "torqx", "float6"), ("0120", "rpma", "int"), ("0121", "sppa", "float6"), ("0122", "chkp", "float6"), ("0123", "spm1", "int"), ("0124", "spm2", "int"), ("0125", "spm3", "int"), ("0126", "tvolact", "float6"), ("0127", "tvolcact", "float6"), ("0128", "mfop", "int"), ("0130", "mfoa", "float6"), ("0131", "mfia", "float6"), ("0132", "mdoa", "float6"), ("0133", "mdia", "float6"), ("0134", "mtoa", "float6"), ("0135", "mtia", "float6"), ("0136", "mcoa", "float6"), ("0137", "stkc", "int"), ("0139", "deptretm", "float6"), ]