From cb13f28d2efd6d41a276cfe0f6668918791acb2f Mon Sep 17 00:00:00 2001
From: gg
Date: Fri, 17 Jan 2025 16:23:24 +0100
Subject: [PATCH 1/3] hikrobot smart camera login ok
---
src/components/hikrobot_sc.py | 2 ++
src/test/hikrobot_sc/test_hikrobot_sc.py | 46 ++++++++++++++++++------
src/ui/test/test.py | 1 -
3 files changed, 38 insertions(+), 11 deletions(-)
create mode 100644 src/components/hikrobot_sc.py
diff --git a/src/components/hikrobot_sc.py b/src/components/hikrobot_sc.py
new file mode 100644
index 0000000..8ec73f5
--- /dev/null
+++ b/src/components/hikrobot_sc.py
@@ -0,0 +1,2 @@
+MV_MAX_DEVICE_NUM=256
+MV_VS_OK=0x00000000
diff --git a/src/test/hikrobot_sc/test_hikrobot_sc.py b/src/test/hikrobot_sc/test_hikrobot_sc.py
index de44caa..beb781c 100644
--- a/src/test/hikrobot_sc/test_hikrobot_sc.py
+++ b/src/test/hikrobot_sc/test_hikrobot_sc.py
@@ -1,10 +1,11 @@
import ctypes
import os
-
+from ctypes import *
# Load the MvVisionSensorControl library
-mv_lib = ctypes.windll.LoadLibrary("bin/MvVisionSensorControl.dll")
+mv_lib = ctypes.windll.LoadLibrary("src/test/hikrobot_sc/bin/MvVisionSensorControl.dll")
+
+from src.components.hikrobot_sc import *
-MV_MAX_DEVICE_NUM=256
# Define the necessary structures and constants from MvVisionSensorDefine.h
class MV_VS_DEVICE_INFO(ctypes.Structure):
_fields_ = [
@@ -27,8 +28,6 @@ class MV_VS_DEVICE_INFO(ctypes.Structure):
("nReserved", ctypes.c_uint * 8), # Reserved
]
-MV_MAX_DEVICE_NUM = 256 # Maximum number of supported devices
-
class MV_VS_DEVICE_INFO_LIST(ctypes.Structure):
_fields_ = [
@@ -37,12 +36,16 @@ class MV_VS_DEVICE_INFO_LIST(ctypes.Structure):
]
-# Define MV_VS_OK from MvVSErrorDefine.h (assuming it's 0)
-MV_VS_OK = 0
-
# Define function signatures
-mv_lib.MV_VS_EnumDevices.argtypes = [ctypes.POINTER(MV_VS_DEVICE_INFO_LIST)]
-mv_lib.MV_VS_EnumDevices.restype = ctypes.c_int
+mv_lib.MV_VS_EnumDevices.argtypes = [POINTER(MV_VS_DEVICE_INFO_LIST)]
+mv_lib.MV_VS_EnumDevices.restype = c_int
+
+mv_lib.MV_VS_CreateHandle.argtypes = [POINTER(c_void_p), c_void_p] # Assuming pHandle is a void* and pstDeviceInfo is a struct
+mv_lib.MV_VS_CreateHandle.restype = c_int
+
+mv_lib.MV_VS_Login.argtypes = [c_void_p, c_char_p, c_char_p]
+mv_lib.MV_VS_Login.restype = c_int
+
def print_device_info(device_info_list):
num_devices=device_info_list.nDeviceNum
@@ -57,6 +60,8 @@ def print_device_info(device_info_list):
def main():
+ global MV_VS_OK
+
print("SC2016 TEST")
device_info_list = MV_VS_DEVICE_INFO_LIST()
@@ -75,6 +80,27 @@ def main():
print("Find No Devices!")
return -2
+ if device_info_list.nDeviceNum == 2:
+ print("Starting dual camera test!")
+ # Define MV_VS_OK (replace with the actual value)
+ MV_VS_OK = 0
+ MV_VS_DEVICE_INFO_1 = device_info_list.pDeviceInfo[0];
+ MV_VS_DEVICE_INFO_2 = device_info_list.pDeviceInfo[1];
+ pHandle1 = c_void_p()
+ pHandle2 = c_void_p()
+ nRet1 = mv_lib.MV_VS_CreateHandle(byref(pHandle1), MV_VS_DEVICE_INFO_1)
+ nRet2 = mv_lib.MV_VS_CreateHandle(byref(pHandle2), MV_VS_DEVICE_INFO_2)
+ if nRet1 != MV_VS_OK or nRet2 != MV_VS_OK:
+ print(f"Create Handle failed! [{nRet1:#x} {nRet2:#x}]")
+ return
+ nRet1 = mv_lib.MV_VS_Login(pHandle1, b"Admin", b"Abc1234")
+ nRet2 = mv_lib.MV_VS_Login(pHandle2, b"Admin", b"Abc1234")
+ if nRet1 != MV_VS_OK or nRet2 != MV_VS_OK:
+ print(f"Login failed! [{nRet1:#x} {nRet2:#x}]")
+ return
+ else:
+ print("Login OK")
+
return 0
if __name__ == "__main__":
diff --git a/src/ui/test/test.py b/src/ui/test/test.py
index bbb35c9..1a045d4 100755
--- a/src/ui/test/test.py
+++ b/src/ui/test/test.py
@@ -6,7 +6,6 @@ import weakref
from datetime import datetime, timedelta
from PyQt5.QtCore import QTimer, pyqtSlot
from PyQt5.QtWidgets import QMessageBox
-from component import Component
from lib.db import Archive, Recipes, Users
from lib.helpers import get_shift
from lib.helpers.step import Step
From 7f7fa062664260a37aeb2ec0d4f26c780c4feea9 Mon Sep 17 00:00:00 2001
From: gg
Date: Fri, 17 Jan 2025 19:17:09 +0100
Subject: [PATCH 2/3] hikrobot smart camera almost getting data
---
src/components/hikrobot_dll.py | 233 +++++++++++++++++++++++
src/components/hikrobot_sc.py | 76 +++++++-
src/test/hikrobot_sc/test_hikrobot_sc.py | 74 +++----
3 files changed, 338 insertions(+), 45 deletions(-)
create mode 100644 src/components/hikrobot_dll.py
diff --git a/src/components/hikrobot_dll.py b/src/components/hikrobot_dll.py
new file mode 100644
index 0000000..c2ce528
--- /dev/null
+++ b/src/components/hikrobot_dll.py
@@ -0,0 +1,233 @@
+import ctypes
+import struct
+from ctypes import *
+import sys
+
+MV_MAX_DEVICE_NUM=256
+MV_VS_OK=0x00000000
+CHUNK_MASK_IMAGE_PORT=60005536
+CHUNK_RESULT_PORT=60005537
+
+mv_lib = ctypes.windll.LoadLibrary("src/test/hikrobot_sc/bin/MvVisionSensorControl.dll")
+
+# define structures
+from ctypes import *
+
+class MvVSGvspPixelType(c_int):
+ MVVS_PixelType_Undefined = 0xFFFFFFFF
+
+ MVVS_PixelType_Mono1p = (0x0037)
+ MVVS_PixelType_Mono2p = (0x0038)
+ MVVS_PixelType_Mono4p = (0x0039)
+ MVVS_PixelType_Mono8 = (0x0001)
+ MVVS_PixelType_Mono8_Signed = (0x0002)
+ MVVS_PixelType_Mono10 = (0x0003)
+ MVVS_PixelType_Mono10_Packed = (0x0004)
+ MVVS_PixelType_Mono12 = (0x0005)
+ MVVS_PixelType_Mono12_Packed = (0x0006)
+ MVVS_PixelType_Mono14 = (0x0025)
+ MVVS_PixelType_Mono16 = (0x0007)
+
+ MVVS_PixelType_BayerGR8 = (0x0008)
+ MVVS_PixelType_BayerRG8 = (0x0009)
+ MVVS_PixelType_BayerGB8 = (0x000A)
+ MVVS_PixelType_BayerBG8 = (0x000B)
+ MVVS_PixelType_BayerGR10 = (0x000C)
+ MVVS_PixelType_BayerRG10 = (0x000D)
+ MVVS_PixelType_BayerGB10 = (0x000E)
+ MVVS_PixelType_BayerBG10 = (0x000F)
+ MVVS_PixelType_BayerGR12 = (0x0010)
+ MVVS_PixelType_BayerRG12 = (0x0011)
+ MVVS_PixelType_BayerGB12 = (0x0012)
+ MVVS_PixelType_BayerBG12 = (0x0013)
+ MVVS_PixelType_BayerGR10_Packed = (0x0026)
+ MVVS_PixelType_BayerRG10_Packed = (0x0027)
+ MVVS_PixelType_BayerGB10_Packed = (0x0028)
+ MVVS_PixelType_BayerBG10_Packed = (0x0029)
+ MVVS_PixelType_BayerGR12_Packed = (0x002A)
+ MVVS_PixelType_BayerRG12_Packed = (0x002B)
+ MVVS_PixelType_BayerGB12_Packed = (0x002C)
+ MVVS_PixelType_BayerBG12_Packed = (0x002D)
+ MVVS_PixelType_BayerGR16 = (0x002E)
+ MVVS_PixelType_BayerRG16 = (0x002F)
+ MVVS_PixelType_BayerGB16 = (0x0030)
+ MVVS_PixelType_BayerBG16 = (0x0031)
+
+ MVVS_PixelType_RGB8_Packed = (0x0014)
+ MVVS_PixelType_BGR8_Packed = (0x0015)
+ MVVS_PixelType_RGBA8_Packed = (0x0016)
+ MVVS_PixelType_BGRA8_Packed = (0x0017)
+ MVVS_PixelType_RGB10_Packed = (0x0018)
+ MVVS_PixelType_BGR10_Packed = (0x0019)
+ MVVS_PixelType_RGB12_Packed = (0x001A)
+ MVVS_PixelType_BGR12_Packed = (0x001B)
+ MVVS_PixelType_RGB16_Packed = (0x0033)
+ MVVS_PixelType_BGR16_Packed = (0x004B)
+ MVVS_PixelType_RGBA16_Packed = (0x0064)
+ MVVS_PixelType_BGRA16_Packed = (0x0051)
+ MVVS_PixelType_RGB10V1_Packed = (0x001C)
+ MVVS_PixelType_RGB10V2_Packed = (0x001D)
+ MVVS_PixelType_RGB12V1_Packed = (0X0034)
+ MVVS_PixelType_RGB565_Packed = (0x0035)
+ MVVS_PixelType_BGR565_Packed = (0X0036)
+
+ MVVS_PixelType_YUV411_Packed = (0x001E)
+ MVVS_PixelType_YUV422_Packed = (0x001F)
+ MVVS_PixelType_YUV422_YUYV_Packed = (0x0032)
+ MVVS_PixelType_YUV444_Packed = (0x0020)
+ MVVS_PixelType_YCBCR8_CBYCR = (0x003A)
+ MVVS_PixelType_YCBCR422_8 = (0x003B)
+ MVVS_PixelType_YCBCR422_8_CBYCRY = (0x0043)
+ MVVS_PixelType_YCBCR411_8_CBYYCRYY = (0x003C)
+ MVVS_PixelType_YCBCR601_8_CBYCR = (0x003D)
+ MVVS_PixelType_YCBCR601_422_8 = (0x003E)
+ MVVS_PixelType_YCBCR601_422_8_CBYCRY = (0x0044)
+ MVVS_PixelType_YCBCR601_411_8_CBYYCRYY = (0x003F)
+ MVVS_PixelType_YCBCR709_8_CBYCR = (0x0040)
+ MVVS_PixelType_YCBCR709_422_8 = (0x0041)
+ MVVS_PixelType_YCBCR709_422_8_CBYCRY = (0x0045)
+ MVVS_PixelType_YCBCR709_411_8_CBYYCRYY = (0x0042)
+
+ MVVS_PixelType_RGB8_Planar = (0x0021)
+ MVVS_PixelType_RGB10_Planar = (0x0022)
+ MVVS_PixelType_RGB12_Planar = (0x0023)
+ MVVS_PixelType_RGB16_Planar = (0x0024)
+
+ MVVS_PixelType_Jpeg = (0x0001)
+
+class MV_VS_DATA(Structure):
+ _fields_ = [
+ ("pImage", POINTER(c_char)), # Pointer of Image
+ ("nImageLen", c_uint), # The Length of Image
+ ("nImageWidth", c_uint), # Image Width
+ ("nImageHeight", c_uint), # Image Height
+ ("nImageNum", c_uint64), # Image Number
+ ("enPixelType", c_int), # Pixel Type (assuming MvVSGvspPixelType is an enum)
+
+ ("nDevTimeStampHigh", c_uint), # Timestamp high 32 bits
+ ("nDevTimeStampLow", c_uint), # Timestamp low 32 bits
+ ("nReserved0", c_uint), # Reserved, 8-byte aligned
+ ("nHostTimeStamp", c_int64), # Host-generated timestamp
+
+ ("pChunkData", POINTER(c_char)), # Chunk data
+ ("nChunkDataLen", c_uint), # Chunk Length
+
+ ("nReserved", c_uint * 64), # Reserved
+ ]
+
+class MV_VS_DEVICE_INFO(ctypes.Structure):
+ _fields_ = [
+ ("nMajorVer", ctypes.c_ushort), # GigE protocol Major Version
+ ("nMinorVer", ctypes.c_ushort), # GigE protocol Minor Version
+ ("nMacAddrHigh", ctypes.c_uint), # High MAC Address
+ ("nMacAddrLow", ctypes.c_uint), # Low MAC Address
+ ("nIpCfgOption", ctypes.c_uint), # Supported IP Configuration Options
+ ("nIpCfgCurrent", ctypes.c_uint), # IP Configuration
+ ("nCurrentIp", ctypes.c_uint), # Current IP
+ ("nCurrentSubNetMask", ctypes.c_uint), # Current Subnet Mask
+ ("nDefultGateWay", ctypes.c_uint), # Current Gateway
+ ("chManufacturerName", ctypes.c_char * 32), # Manufacturer Name
+ ("chModelName", ctypes.c_char * 32), # Model Name
+ ("chDeviceVersion", ctypes.c_char * 32), # Device Version
+ ("chManufacturerSpecificInfo", ctypes.c_char * 48), # Manufacturer Specific Info
+ ("chSerialNumber", ctypes.c_char * 16), # Serial Number
+ ("chUserDefinedName", ctypes.c_char * 16), # User Defined Name
+ ("nNetExport", ctypes.c_uint), # IP address of connected network port
+ ("nReserved", ctypes.c_uint * 8), # Reserved
+ ]
+
+
+class MV_VS_DEVICE_INFO_LIST(ctypes.Structure):
+ _fields_ = [
+ ("nDeviceNum", ctypes.c_uint), # Online Device Number
+ ("pDeviceInfo", ctypes.POINTER(MV_VS_DEVICE_INFO) * MV_MAX_DEVICE_NUM), # Device info array
+ ]
+
+
+# Define function signatures
+
+mv_lib.MV_VS_GetResultData.argtypes = [c_void_p, POINTER(MV_VS_DATA), c_int]
+mv_lib.MV_VS_GetResultData.restype = c_int
+
+mv_lib.MV_VS_ReleaseResultData.argtypes = [ctypes.c_void_p, ctypes.POINTER(MV_VS_DATA)]
+mv_lib.MV_VS_ReleaseResultData.restype = ctypes.c_int
+
+mv_lib.MV_VS_EnumDevices.argtypes = [POINTER(MV_VS_DEVICE_INFO_LIST)]
+mv_lib.MV_VS_EnumDevices.restype = c_int
+
+mv_lib.MV_VS_CreateHandle.argtypes = [POINTER(c_void_p), c_void_p] # Assuming pHandle is a void* and pstDeviceInfo is a struct
+mv_lib.MV_VS_CreateHandle.restype = c_int
+
+mv_lib.MV_VS_Login.argtypes = [c_void_p, c_char_p, c_char_p]
+mv_lib.MV_VS_Login.restype = c_int
+
+mv_lib.MV_VS_SetBoolValue.argtypes = [c_void_p, c_char_p, c_bool]
+mv_lib.MV_VS_SetBoolValue.restype = c_int
+
+mv_lib.MV_VS_SetEnumValue.argtypes = [c_void_p, c_char_p, c_uint]
+mv_lib.MV_VS_SetEnumValue.restype = c_int
+
+mv_lib.MV_VS_StartRun.argtypes = [c_void_p] # handle
+mv_lib.MV_VS_StartRun.restype = c_int
+
+
+def MV_VS_GetFrame(handle):
+ nRet = MV_VS_OK
+ stFrameData = MV_VS_DATA()
+
+
+ nRet = mv_lib.MV_VS_GetResultData(handle, byref(stFrameData), 2000)
+ if nRet == MV_VS_OK:
+ # Print frame information
+ print(
+ f"Frame Width: [{stFrameData.nImageWidth}], Height: [{stFrameData.nImageHeight}], ImageLen: [{stFrameData.nImageLen}]")
+
+ # Process chunk data
+ currentDataTag = 0
+ while stFrameData.nChunkDataLen > currentDataTag:
+ chunkLength = \
+ struct.unpack_from(" stFrameData.nChunkDataLen - 8 - currentDataTag:
+ # Handle exception
+ break
+
+ if chunkID == CHUNK_RESULT_PORT: # Result data in JSON format
+ chResultInfo = ctypes.cast(
+ stFrameData.pChunkData + (stFrameData.nChunkDataLen - 8 - chunkLength - currentDataTag),
+ ctypes.POINTER(ctypes.c_char))
+ print(f"chResultInfo [{chResultInfo.value.decode()}] end")
+
+ elif chunkID == CHUNK_MASK_IMAGE_PORT: # Mask image data
+ maskModID = struct.unpack_from(" currentDataTag:
+ chunkLength = struct.unpack_from(" stFrameData.nChunkDataLen - 8 - currentDataTag:
+ # Handle exception
+ break
+
+ if chunkID == CHUNK_RESULT_PORT: # Result data in JSON format
+ chResultInfo = ctypes.cast(stFrameData.pChunkData + (stFrameData.nChunkDataLen - 8 - chunkLength - currentDataTag), ctypes.POINTER(ctypes.c_char))
+ print(f"chResultInfo [{chResultInfo.value.decode()}] end")
+
+ elif chunkID == CHUNK_MASK_IMAGE_PORT: # Mask image data
+ maskModID = struct.unpack_from("
Date: Tue, 21 Jan 2025 10:52:13 +0100
Subject: [PATCH 3/3] dev
---
src/components/pipe_cutter_component.py | 87 +++++++--------------
src/ui/test_pipe_cutter/test_pipe_cutter.py | 6 +-
2 files changed, 32 insertions(+), 61 deletions(-)
diff --git a/src/components/pipe_cutter_component.py b/src/components/pipe_cutter_component.py
index 2022552..dfc2791 100644
--- a/src/components/pipe_cutter_component.py
+++ b/src/components/pipe_cutter_component.py
@@ -1,11 +1,9 @@
-import logging
-import time
-
from .modbus_component import ModbusComponent
+import logging
class PipeCutterComponent(ModbusComponent):
- def __init__(self, config=None, name="pipe_cutter", period=1, lazy=True, paused=False, threaded=True):
+ def __init__(self, config=None, name=None, period=1, lazy=True, paused=False, threaded=True):
"""Initialize the Pipe Cutter Component."""
super().__init__(config=config, name=name, period=period, lazy=lazy, paused=paused, threaded=threaded)
self.log = logging.getLogger(self.name)
@@ -13,74 +11,49 @@ class PipeCutterComponent(ModbusComponent):
self.current_od_of_pipe = None
self.machine_status = None
+ def config_changed(self):
+ super().config_changed()
+
+
def read_total_length(self):
- """Read the total length (Register 4X30)."""
+ """Read the total length from Register 4X30."""
try:
- self.current_total_length = self.read(register_address=30, data_type="16bit_uint")
- self.log.info(f"Total length (Register 4X30): {self.current_total_length}")
+ self.current_total_length = self._read(register=30)
+ self.log.info(f"Total Length Read (Register 4X30): {self.current_total_length}")
return self.current_total_length
except Exception as e:
- self.log.error(f"Failed to read total length (Register 4X30): {e}")
+ self.log.error(f"Error reading total length: {e}")
raise
def read_od_of_pipe(self):
- """Read the OD of the pipe (Register 4X60)."""
+ """Read the OD of the pipe from Register 4X60."""
try:
- self.current_od_of_pipe = self.read(register_address=60, data_type="16bit_uint")
- self.log.info(f"OD of the pipe (Register 4X60): {self.current_od_of_pipe}")
+ self.current_od_of_pipe = self._read(register=60)
+ self.log.info(f"OD of Pipe Read (Register 4X60): {self.current_od_of_pipe}")
return self.current_od_of_pipe
except Exception as e:
- self.log.error(f"Failed to read OD of the pipe (Register 4X60): {e}")
+ self.log.error(f"Error reading OD of pipe: {e}")
raise
def read_machine_status(self):
+ """Read the machine status from Register 4X766."""
try:
- self.machine_status = self.read(register_address=1,register=766 ,data_type="16bit_uint")
- self.log.info(f"Machine status (Register 4X766): {self.machine_status}")
+ if not hasattr(self, 'client') or self.client is None:
+ self.log.error(
+ "Attempting to read machine status but 'client' is not initialized.")
+ raise AttributeError(
+ "'PipeCutterComponent' object has no attribute 'client'.")
+
+ self.machine_status = self._read(register=766)
+ self.log.info(f"Machine Status Read (Register 4X766): {self.machine_status}")
return self.machine_status
- except Exception as e:
- self.log.error(f"Failed to read machine status (Register 4X766): {e}")
+ except AttributeError as ae:
+ # Log more detailed instance state for debugging
+ self.log.error(
+ f"AttributeError while reading machine status: {ae}. Component State: "
+ f"name={self.name}, config={self.config}, client={getattr(self, 'client', None)}"
+ )
raise
-
- def write_total_length(self, value):
- try:
- self.write(register_address=1,register=30, value=value, data_type="16bit_uint")
- self.log.info(f"Wrote total length (Register 4X30): {value}")
except Exception as e:
- self.log.error(f"Failed to write total length (Register 4X30): {e}")
+ self.log.error(f"Error reading machine status: {e}")
raise
-
- def write_od_of_pipe(self, value):
- try:
- self.write(register_address=1,register=60, value=value, data_type="16bit_uint")
- self.log.info(f"Wrote OD of pipe (Register 4X60): {value}")
- except Exception as e:
- self.log.error(f"Failed to write OD of pipe (Register 4X60): {e}")
- raise
-
- def write_bit_with_delay(self, register_address, bit_position, delay_ms):
- """
- Set a specific bit in a Modbus register, wait for a specified time, and reset it.
-
- :param register_address: The register address to write to.
- :param bit_position: The bit position to modify (0-15).
- :param delay_ms: The delay in milliseconds before resetting the bit to 0 .
- """
- try:
- # Step 1: Set the bit to 1
- current_value = self.read(register_address, data_type="16bit_uint")
- new_value = current_value | (1 << bit_position) # Set the bit
- self.write(register_address, new_value, data_type="16bit_uint")
- self.log.info(f"Set bit {bit_position} in register {register_address} to 1")
-
- # Step 2: Wait for the specified delay
- time.sleep(delay_ms / 1000) # Convert milliseconds to seconds
-
- # Step 3: Reset the bit to 0
- current_value = self.read(register_address, data_type="16bit_uint")
- new_value = current_value & ~(1 << bit_position) # Clear the bit
- self.write(register_address, new_value, data_type="16bit_uint")
- self.log.info(f"Reset bit {bit_position} in register {register_address} to 0")
- except Exception as e:
- self.log.error(f"Failed to set and reset bit {bit_position} in register {register_address}: {e}")
- raise
\ No newline at end of file
diff --git a/src/ui/test_pipe_cutter/test_pipe_cutter.py b/src/ui/test_pipe_cutter/test_pipe_cutter.py
index 5f62bb4..6d4b515 100644
--- a/src/ui/test_pipe_cutter/test_pipe_cutter.py
+++ b/src/ui/test_pipe_cutter/test_pipe_cutter.py
@@ -11,10 +11,8 @@ class Test_Pipe_Cutter(Test_Test):
A class to manage and operate a pipe cutter machine in a test environment.
"""
- def __init__(self, config, components, recipe=None, run_once=False,
- reset_on_start=False, enable_override=False, bench_name="generic", step=None,):
- super().__init__(components=components, recipe=recipe, step=None, run_once=run_once,
- reset_on_start=reset_on_start, enable_override=enable_override,)
+ def __init__(self, config, components, recipe=None, run_once=False,reset_on_start=False, enable_override=False, bench_name="generic", step=None,):
+ super().__init__(components=components, recipe=recipe, step=None, run_once=run_once,reset_on_start=reset_on_start, enable_override=enable_override,)
self.bench_name = bench_name
self.cutter = PipeCutterComponent()
self.current_cut_length = 0.0