ndtkit-api 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ndtkit_api-0.0.1/LICENSE +42 -0
- ndtkit_api-0.0.1/PKG-INFO +127 -0
- ndtkit_api-0.0.1/README.md +111 -0
- ndtkit_api-0.0.1/ndtkit_api/NDTKitAScanInterface.py +6 -0
- ndtkit_api-0.0.1/ndtkit_api/NDTKitCScanInterface.py +32 -0
- ndtkit_api-0.0.1/ndtkit_api/config/__init__.py +14 -0
- ndtkit_api-0.0.1/ndtkit_api/model/ascan/NIAScanGate.py +11 -0
- ndtkit_api-0.0.1/ndtkit_api/model/frame/NICartographyFrame.py +48 -0
- ndtkit_api-0.0.1/ndtkit_api/model/frame/NICartographyFrameAScan.py +91 -0
- ndtkit_api-0.0.1/ndtkit_api/model/frame/NICartographyFrameCScan.py +202 -0
- ndtkit_api-0.0.1/ndtkit_api/model/scans/ascan/NIAScan.py +29 -0
- ndtkit_api-0.0.1/ndtkit_api/ndtkit_socket_connection.py +121 -0
- ndtkit_api-0.0.1/ndtkit_api.egg-info/PKG-INFO +127 -0
- ndtkit_api-0.0.1/ndtkit_api.egg-info/SOURCES.txt +17 -0
- ndtkit_api-0.0.1/ndtkit_api.egg-info/dependency_links.txt +1 -0
- ndtkit_api-0.0.1/ndtkit_api.egg-info/requires.txt +2 -0
- ndtkit_api-0.0.1/ndtkit_api.egg-info/top_level.txt +1 -0
- ndtkit_api-0.0.1/pyproject.toml +23 -0
- ndtkit_api-0.0.1/setup.cfg +4 -0
ndtkit_api-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
Copyright (c) 2025 TESTIA SAS. All Rights Reserved.
|
|
2
|
+
|
|
3
|
+
==============================================================================
|
|
4
|
+
PROPRIETARY SOFTWARE LICENSE
|
|
5
|
+
for the ndtkit_api
|
|
6
|
+
==============================================================================
|
|
7
|
+
|
|
8
|
+
This Application Programming Interface, including its documentation, data structures, and all related materials (collectively, the "API"), is proprietary software and the confidential intellectual property of TESTIA SAS ("Licensor").
|
|
9
|
+
|
|
10
|
+
**1. LICENSE REQUIREMENT**
|
|
11
|
+
|
|
12
|
+
Access to, and use of, this API is strictly conditional upon and requires that you (or the legal entity you represent) hold a valid, active, and fully paid commercial software license for the "NDTkit" software (the "NDTkit License").
|
|
13
|
+
|
|
14
|
+
Any access or use of this API without a valid NDTkit License is unauthorized, constitutes a material breach of this agreement, and is a violation of Licensor's intellectual property rights.
|
|
15
|
+
|
|
16
|
+
**2. GOVERNING AGREEMENT**
|
|
17
|
+
|
|
18
|
+
Your use of this API is governed by the terms and conditions of your NDTkit License agreement, or a separate API Terms of Service provided by Licensor, whichever is applicable (the "Governing Agreement"). By accessing or using this API, you represent and warrant that you hold a valid NDTkit License and you agree to be bound by the terms of the Governing Agreement.
|
|
19
|
+
|
|
20
|
+
**3. TERMINATION OF RIGHTS**
|
|
21
|
+
|
|
22
|
+
Your right to use this API will terminate immediately and automatically, without notice, if your NDTkit License expires, is terminated, or is otherwise no longer valid. Upon such termination, you must cease all use of the API and destroy any related materials in your possession.
|
|
23
|
+
|
|
24
|
+
**4. RESTRICTIONS**
|
|
25
|
+
|
|
26
|
+
You may not, without express written permission from Licensor:
|
|
27
|
+
a) Sublicense, sell, rent, lease, or otherwise distribute the API to any third party.
|
|
28
|
+
b) Reverse engineer, decompile, disassemble, or attempt to derive the source code of the API.
|
|
29
|
+
c) Use the API to create a product or service that competes with the NDTkit software or the API itself.
|
|
30
|
+
d) Use the API for any purpose not expressly permitted by your Governing Agreement.
|
|
31
|
+
|
|
32
|
+
**5. NO OTHER RIGHTS GRANTED**
|
|
33
|
+
|
|
34
|
+
This license file does not grant you any rights to use, copy, modify, or distribute the API. All rights not expressly granted herein are reserved by TESTIA SAS.
|
|
35
|
+
|
|
36
|
+
**6. DISCLAIMER OF WARRANTY**
|
|
37
|
+
|
|
38
|
+
THE API IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.
|
|
39
|
+
|
|
40
|
+
**7. LIMITATION OF LIABILITY**
|
|
41
|
+
|
|
42
|
+
IN NO EVENT SHALL TESTIA SAS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE API OR THE USE OR OTHER DEALINGS IN THE API.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ndtkit_api
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: This Python API provides a high-level interface to interact with the NDTkit desktop application. It allows developers to script and automate NDTkit features remotely, leveraging the full power of the underlying Java application through a simple and pythonic library.
|
|
5
|
+
Author-email: Cédric BERTRAND <cedric.bertrand@testia.com>
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Operating System :: OS Independent
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Topic :: Software Development
|
|
10
|
+
Requires-Python: >=3.11.9
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: requests
|
|
14
|
+
Requires-Dist: py4j
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# NDTkit Python API
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+

|
|
21
|
+

|
|
22
|
+
|
|
23
|
+
This Python API provides a high-level interface to interact with the NDTkit desktop application. It allows developers to script and automate NDTkit features remotely, leveraging the full power of the underlying Java application through a simple and pythonic library.
|
|
24
|
+
|
|
25
|
+
## How it Works ⚙️
|
|
26
|
+
|
|
27
|
+
The API acts as a client that communicates with a dedicated socket server running within the NDTkit Java application.
|
|
28
|
+
|
|
29
|
+
1. **Python Call**: A function is called in Python (e.g., `NDTKitCScanInterface.open_cscan(...)`).
|
|
30
|
+
2. **JSON Serialization**: The call (class name, method name, parameters) is serialized into a JSON object.
|
|
31
|
+
3. **Socket Communication**: The JSON payload is sent over a TCP socket to the Java server.
|
|
32
|
+
4. **Java Execution**: The server deserializes the request, invokes the corresponding Java API method using reflection, and waits for the result.
|
|
33
|
+
5. **Return Value**: The result from the Java method is serialized back to JSON and sent to the Python client, which deserializes it into a Python object (like a `NICartographyFrameCScan` instance).
|
|
34
|
+
|
|
35
|
+
## Prerequisites
|
|
36
|
+
|
|
37
|
+
Before using this API, you must have:
|
|
38
|
+
|
|
39
|
+
1. **Python 3.9+** installed.
|
|
40
|
+
2. The **NDTkit desktop application** installed.
|
|
41
|
+
3. The Java socket server for the API must be running. The Python API can automatically launch it if configured correctly.
|
|
42
|
+
|
|
43
|
+
## 🚀 Installation & Configuration
|
|
44
|
+
|
|
45
|
+
1. **Clone the repository** (or add it to your project). This library is not on PyPI.
|
|
46
|
+
|
|
47
|
+
2. **Configure the connection**: At the root of the `apipython` package, you will find a `config` directory. You need to edit the `config.ini` file.
|
|
48
|
+
|
|
49
|
+
```ini
|
|
50
|
+
[Parameters]
|
|
51
|
+
SERVER_HOST = 127.0.0.1
|
|
52
|
+
SERVER_PORT = 32146
|
|
53
|
+
MESSAGE_TYPE = 10
|
|
54
|
+
|
|
55
|
+
# IMPORTANT: Update this path to point to your serverMode script
|
|
56
|
+
SOCKET_SERVER_COMMAND = <NDTKIT_INSTALL_FOLDER>/NDTkit.vbs # Alternative: <NDTKIT_INSTALL_FOLDER>/modules/serverMode/serverMode.bat
|
|
57
|
+
|
|
58
|
+
SERVER_CONNECTION_TIMEOUT = 120
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- **`SERVER_HOST`**: The IP address of the machine running NDTkit. Use `127.0.0.1` if it's on the same machine.
|
|
62
|
+
- **`SERVER_PORT`**: The port for the socket communication. This must match the port the NDTkit server is listening on.
|
|
63
|
+
- **`SOCKET_SERVER_COMMAND`**: **This is the most critical setting.** It's the command used to launch the Java socket server. The Python API will execute this command if it cannot connect to the server.
|
|
64
|
+
|
|
65
|
+
## Usage Example
|
|
66
|
+
|
|
67
|
+
Here is a simple example showing how to open a C-Scan, modify a pixel, and save it to a new file.
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
# examples.py
|
|
71
|
+
|
|
72
|
+
import NDTKitCScanInterface
|
|
73
|
+
from model.frame.NICartographyFrameCScan import NICartographyFrameCScan
|
|
74
|
+
|
|
75
|
+
def change_pixel_value(input_file: str, output_file: str):
|
|
76
|
+
"""
|
|
77
|
+
Opens a C-Scan, modifies the value of the first pixel, saves it,
|
|
78
|
+
and re-opens it for display.
|
|
79
|
+
"""
|
|
80
|
+
print(f"Opening C-Scan: {input_file}")
|
|
81
|
+
# The server is launched automatically if not running
|
|
82
|
+
cscan = NDTKitCScanInterface.openCScan(input_file)
|
|
83
|
+
|
|
84
|
+
if not cscan:
|
|
85
|
+
print("Failed to open C-Scan.")
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
print(f"C-Scan opened successfully with UUID: {cscan.get_uuid()}")
|
|
89
|
+
|
|
90
|
+
print("Retrieving data matrix...")
|
|
91
|
+
data = cscan.get_data()
|
|
92
|
+
print(f"Original value at [0][0]: {data[0][0]}")
|
|
93
|
+
|
|
94
|
+
# Modify the data
|
|
95
|
+
data[0][0] = 12.0
|
|
96
|
+
print(f"Setting new value at [0][0] to 12.0")
|
|
97
|
+
cscan.set_data(data)
|
|
98
|
+
|
|
99
|
+
print(f"Saving modified C-Scan to: {output_file}")
|
|
100
|
+
NDTKitCScanInterface.saveCscan(cscan, output_file)
|
|
101
|
+
|
|
102
|
+
print("Re-opening the saved C-Scan to verify.")
|
|
103
|
+
NDTKitCScanInterface.openCScan(output_file, displayResult=True)
|
|
104
|
+
print("Done.")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
if __name__ == "__main__":
|
|
108
|
+
# Make sure to use paths that are valid on your system
|
|
109
|
+
input_cscan_path = "D:/Data/NKC/dd_carto.nkc"
|
|
110
|
+
output_cscan_path = "D:/tmp/modified_cscan.nkc"
|
|
111
|
+
change_pixel_value(input_cscan_path, output_cscan_path)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## A class/method available in the Java API which is not appearing in this Python API
|
|
115
|
+
|
|
116
|
+
It can reachable anyway, it's just the auto-completion of your IDE that is not working. Actually this Python API is using [py4j](https://www.py4j.org/) that allows to access any Java API function.
|
|
117
|
+
Here's an example on how to access to `agi.ndtkit.api.NDTKitCScanInterface.openCScan(String)` without using this wrapper project:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from ndtkit_socket_connection import gateway
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
gateway.jvm.agi.ndtkit.api.NDTKitCScanInterface.openCScan("path/to/my/cscan")
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
So please refers to the NDTkit Javadoc to have an exhaustive look at all the available actions and call them using this solution if they are not available directly in this Python API.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# NDTkit Python API
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
This Python API provides a high-level interface to interact with the NDTkit desktop application. It allows developers to script and automate NDTkit features remotely, leveraging the full power of the underlying Java application through a simple and pythonic library.
|
|
8
|
+
|
|
9
|
+
## How it Works ⚙️
|
|
10
|
+
|
|
11
|
+
The API acts as a client that communicates with a dedicated socket server running within the NDTkit Java application.
|
|
12
|
+
|
|
13
|
+
1. **Python Call**: A function is called in Python (e.g., `NDTKitCScanInterface.open_cscan(...)`).
|
|
14
|
+
2. **JSON Serialization**: The call (class name, method name, parameters) is serialized into a JSON object.
|
|
15
|
+
3. **Socket Communication**: The JSON payload is sent over a TCP socket to the Java server.
|
|
16
|
+
4. **Java Execution**: The server deserializes the request, invokes the corresponding Java API method using reflection, and waits for the result.
|
|
17
|
+
5. **Return Value**: The result from the Java method is serialized back to JSON and sent to the Python client, which deserializes it into a Python object (like a `NICartographyFrameCScan` instance).
|
|
18
|
+
|
|
19
|
+
## Prerequisites
|
|
20
|
+
|
|
21
|
+
Before using this API, you must have:
|
|
22
|
+
|
|
23
|
+
1. **Python 3.9+** installed.
|
|
24
|
+
2. The **NDTkit desktop application** installed.
|
|
25
|
+
3. The Java socket server for the API must be running. The Python API can automatically launch it if configured correctly.
|
|
26
|
+
|
|
27
|
+
## 🚀 Installation & Configuration
|
|
28
|
+
|
|
29
|
+
1. **Clone the repository** (or add it to your project). This library is not on PyPI.
|
|
30
|
+
|
|
31
|
+
2. **Configure the connection**: At the root of the `apipython` package, you will find a `config` directory. You need to edit the `config.ini` file.
|
|
32
|
+
|
|
33
|
+
```ini
|
|
34
|
+
[Parameters]
|
|
35
|
+
SERVER_HOST = 127.0.0.1
|
|
36
|
+
SERVER_PORT = 32146
|
|
37
|
+
MESSAGE_TYPE = 10
|
|
38
|
+
|
|
39
|
+
# IMPORTANT: Update this path to point to your serverMode script
|
|
40
|
+
SOCKET_SERVER_COMMAND = <NDTKIT_INSTALL_FOLDER>/NDTkit.vbs # Alternative: <NDTKIT_INSTALL_FOLDER>/modules/serverMode/serverMode.bat
|
|
41
|
+
|
|
42
|
+
SERVER_CONNECTION_TIMEOUT = 120
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
- **`SERVER_HOST`**: The IP address of the machine running NDTkit. Use `127.0.0.1` if it's on the same machine.
|
|
46
|
+
- **`SERVER_PORT`**: The port for the socket communication. This must match the port the NDTkit server is listening on.
|
|
47
|
+
- **`SOCKET_SERVER_COMMAND`**: **This is the most critical setting.** It's the command used to launch the Java socket server. The Python API will execute this command if it cannot connect to the server.
|
|
48
|
+
|
|
49
|
+
## Usage Example
|
|
50
|
+
|
|
51
|
+
Here is a simple example showing how to open a C-Scan, modify a pixel, and save it to a new file.
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
# examples.py
|
|
55
|
+
|
|
56
|
+
import NDTKitCScanInterface
|
|
57
|
+
from model.frame.NICartographyFrameCScan import NICartographyFrameCScan
|
|
58
|
+
|
|
59
|
+
def change_pixel_value(input_file: str, output_file: str):
|
|
60
|
+
"""
|
|
61
|
+
Opens a C-Scan, modifies the value of the first pixel, saves it,
|
|
62
|
+
and re-opens it for display.
|
|
63
|
+
"""
|
|
64
|
+
print(f"Opening C-Scan: {input_file}")
|
|
65
|
+
# The server is launched automatically if not running
|
|
66
|
+
cscan = NDTKitCScanInterface.openCScan(input_file)
|
|
67
|
+
|
|
68
|
+
if not cscan:
|
|
69
|
+
print("Failed to open C-Scan.")
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
print(f"C-Scan opened successfully with UUID: {cscan.get_uuid()}")
|
|
73
|
+
|
|
74
|
+
print("Retrieving data matrix...")
|
|
75
|
+
data = cscan.get_data()
|
|
76
|
+
print(f"Original value at [0][0]: {data[0][0]}")
|
|
77
|
+
|
|
78
|
+
# Modify the data
|
|
79
|
+
data[0][0] = 12.0
|
|
80
|
+
print(f"Setting new value at [0][0] to 12.0")
|
|
81
|
+
cscan.set_data(data)
|
|
82
|
+
|
|
83
|
+
print(f"Saving modified C-Scan to: {output_file}")
|
|
84
|
+
NDTKitCScanInterface.saveCscan(cscan, output_file)
|
|
85
|
+
|
|
86
|
+
print("Re-opening the saved C-Scan to verify.")
|
|
87
|
+
NDTKitCScanInterface.openCScan(output_file, displayResult=True)
|
|
88
|
+
print("Done.")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if __name__ == "__main__":
|
|
92
|
+
# Make sure to use paths that are valid on your system
|
|
93
|
+
input_cscan_path = "D:/Data/NKC/dd_carto.nkc"
|
|
94
|
+
output_cscan_path = "D:/tmp/modified_cscan.nkc"
|
|
95
|
+
change_pixel_value(input_cscan_path, output_cscan_path)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## A class/method available in the Java API which is not appearing in this Python API
|
|
99
|
+
|
|
100
|
+
It can reachable anyway, it's just the auto-completion of your IDE that is not working. Actually this Python API is using [py4j](https://www.py4j.org/) that allows to access any Java API function.
|
|
101
|
+
Here's an example on how to access to `agi.ndtkit.api.NDTKitCScanInterface.openCScan(String)` without using this wrapper project:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from ndtkit_socket_connection import gateway
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
gateway.jvm.agi.ndtkit.api.NDTKitCScanInterface.openCScan("path/to/my/cscan")
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
So please refers to the NDTkit Javadoc to have an exhaustive look at all the available actions and call them using this solution if they are not available directly in this Python API.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
from .ndtkit_socket_connection import gateway
|
|
2
|
+
from .model.frame.NICartographyFrameAScan import NICartographyFrameAScan
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def openAScan(ascanFilePath: str, scanId: int = -1, displayResult: bool = True) -> NICartographyFrameAScan:
|
|
6
|
+
return NICartographyFrameAScan(gateway.jvm.agi.ndtkit.api.NDTKitAScanInterface.openAScan(ascanFilePath, scanId, displayResult))
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from .ndtkit_socket_connection import gateway, call_api_method
|
|
2
|
+
from .model.frame.NICartographyFrameCScan import NICartographyFrameCScan
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def openCScan(cscanFilePath: str, displayResult: bool = True) -> NICartographyFrameCScan:
|
|
6
|
+
return NICartographyFrameCScan(gateway.jvm.agi.ndtkit.api.NDTKitCScanInterface.openCScan(cscanFilePath, displayResult)[0])
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def saveCscan(cscan: NICartographyFrameCScan, filepath: str):
|
|
10
|
+
return gateway.jvm.agi.ndtkit.api.NDTKitCScanInterface.saveCscan(cscan._java_object, filepath)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def createCscan(data: list[list[float]], acquisition_name: str, x_res: float, y_res: float) -> NICartographyFrameCScan:
|
|
14
|
+
parameters = [
|
|
15
|
+
{
|
|
16
|
+
"type": "float[][]",
|
|
17
|
+
"value": str(data)
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"type": "java.lang.String",
|
|
21
|
+
"value": acquisition_name
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"type": "float",
|
|
25
|
+
"value": x_res
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"type": "float",
|
|
29
|
+
"value": y_res
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
return call_api_method("agi.ndtkit.api", "NDTKitCScanInterface", "createCscan", parameters)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import configparser
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
config = configparser.ConfigParser()
|
|
5
|
+
|
|
6
|
+
config.read(Path(__file__).parent / 'config.ini')
|
|
7
|
+
|
|
8
|
+
SERVER_HOST = config.get('Parameters', 'SERVER_HOST', fallback="127.0.0.1")
|
|
9
|
+
SERVER_PORT = config.getint('Parameters', 'SERVER_PORT', fallback=32146)
|
|
10
|
+
MESSAGE_TYPE = config.getint('Parameters', 'MESSAGE_TYPE', fallback=10)
|
|
11
|
+
|
|
12
|
+
# Command to launch NDTkit's socket server.
|
|
13
|
+
SOCKET_SERVER_COMMAND = config.get('Parameters', 'SOCKET_SERVER_COMMAND', fallback="C:/Program Files/NDTkit/modules/serverMode/serverMode.vbs")
|
|
14
|
+
SERVER_CONNECTION_TIMEOUT = config.getint('Parameters', 'SERVER_CONNECTION_TIMEOUT', fallback=120)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from py4j.java_gateway import JavaObject
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class NIAScanGate:
|
|
5
|
+
"""Base class for NIAScanGate, generated from Java API."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, java_object: JavaObject):
|
|
8
|
+
self._java_object = java_object
|
|
9
|
+
|
|
10
|
+
def set_velocity(self, us_velocity: float) -> None:
|
|
11
|
+
return self._java_object.setVelocity(us_velocity)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from py4j.java_gateway import JavaObject
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class NICartographyFrame:
|
|
5
|
+
def __init__(self, java_object: JavaObject):
|
|
6
|
+
self._java_object = java_object
|
|
7
|
+
|
|
8
|
+
def minimize_frame(self, is_minimum: bool):
|
|
9
|
+
return self._java_object.minimizeFrame(is_minimum)
|
|
10
|
+
|
|
11
|
+
def set_title(self, title: str):
|
|
12
|
+
return self._java_object.setTitle(title)
|
|
13
|
+
|
|
14
|
+
def get_scan_type(self):
|
|
15
|
+
return self._java_object.getScanType()
|
|
16
|
+
|
|
17
|
+
def maximize_frame(self, is_maximum: bool):
|
|
18
|
+
return self._java_object.maximizeFrame(is_maximum)
|
|
19
|
+
|
|
20
|
+
def get_projection_cuboid(self):
|
|
21
|
+
return self._java_object.getProjectionCuboid()
|
|
22
|
+
|
|
23
|
+
def get_size(self):
|
|
24
|
+
return self._java_object.getSize()
|
|
25
|
+
|
|
26
|
+
def get_title(self):
|
|
27
|
+
return self._java_object.getTitle()
|
|
28
|
+
|
|
29
|
+
def json_of_scan_reader_parameter(self):
|
|
30
|
+
return self._java_object.jsonOfScanReaderParameter()
|
|
31
|
+
|
|
32
|
+
def get_uuid(self):
|
|
33
|
+
return str(self._java_object.getUUID())
|
|
34
|
+
|
|
35
|
+
def get_file_path(self):
|
|
36
|
+
return self._java_object.getFilePath()
|
|
37
|
+
|
|
38
|
+
def set_file_path(self, filepath: str):
|
|
39
|
+
return self._java_object.setFilePath(filepath)
|
|
40
|
+
|
|
41
|
+
def change_size(self, width: int, height: int):
|
|
42
|
+
return self._java_object.changeSize(width, height)
|
|
43
|
+
|
|
44
|
+
def close(self):
|
|
45
|
+
return self._java_object.close()
|
|
46
|
+
|
|
47
|
+
def get_ifi(self):
|
|
48
|
+
return self._java_object.getIfi()
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from .NICartographyFrame import NICartographyFrame
|
|
2
|
+
from ...model.scans.ascan.NIAScan import NIAScan
|
|
3
|
+
from py4j.java_gateway import JavaObject
|
|
4
|
+
from typing import Any, List
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class NICartographyFrameAScan(NICartographyFrame):
|
|
8
|
+
"""Base class for NICartographyFrameAScan, generated from Java API."""
|
|
9
|
+
|
|
10
|
+
def __init__(self, java_object: JavaObject):
|
|
11
|
+
super().__init__(java_object)
|
|
12
|
+
self._java_object = java_object
|
|
13
|
+
|
|
14
|
+
def get_file_path(self) -> str:
|
|
15
|
+
return self._java_object.getFilePath()
|
|
16
|
+
|
|
17
|
+
def get_raw_data_values(self, row: int, column: int) -> NIAScan:
|
|
18
|
+
return self._java_object.getRawDataValues(row, column)
|
|
19
|
+
|
|
20
|
+
def get_raw_data_values_no_factor(self, row: int, column: int) -> NIAScan:
|
|
21
|
+
return self._java_object.getRawDataValuesNoFactor(row, column)
|
|
22
|
+
|
|
23
|
+
def get_ascan_length(self) -> int:
|
|
24
|
+
return self._java_object.getAscanLength()
|
|
25
|
+
|
|
26
|
+
def get_row(self, row_index: int) -> list[NIAScan]:
|
|
27
|
+
row_of_ascans = self._java_object.getRow(row_index)
|
|
28
|
+
return [NIAScan(ascan) for ascan in row_of_ascans]
|
|
29
|
+
|
|
30
|
+
def get_column(self, row_column: int) -> List[Any]:
|
|
31
|
+
return self._java_object.getColumn(row_column)
|
|
32
|
+
|
|
33
|
+
def get_data_values(self, row: int, column: int) -> NIAScan:
|
|
34
|
+
return self._java_object.getDataValues(row, column)
|
|
35
|
+
|
|
36
|
+
def get_row_number(self) -> int:
|
|
37
|
+
return self._java_object.getRowNumber()
|
|
38
|
+
|
|
39
|
+
def get_column_number(self) -> int:
|
|
40
|
+
return self._java_object.getColumnNumber()
|
|
41
|
+
|
|
42
|
+
def set_file_path(self, filepath: str) -> None:
|
|
43
|
+
return self._java_object.setFilePath(filepath)
|
|
44
|
+
|
|
45
|
+
def get_x_resolution(self) -> float:
|
|
46
|
+
return self._java_object.getXResolution()
|
|
47
|
+
|
|
48
|
+
def get_y_resolution(self) -> float:
|
|
49
|
+
return self._java_object.getYResolution()
|
|
50
|
+
|
|
51
|
+
def get_num_rate(self) -> float:
|
|
52
|
+
return self._java_object.getNumRate()
|
|
53
|
+
|
|
54
|
+
def set_full_range_bscan(self) -> None:
|
|
55
|
+
return self._java_object.setFullRangeBScan()
|
|
56
|
+
|
|
57
|
+
def get_ascan_gates(self):
|
|
58
|
+
return self._java_object.getAScanGates()
|
|
59
|
+
|
|
60
|
+
def get_bscan_gate(self):
|
|
61
|
+
return self._java_object.getBScanGate()
|
|
62
|
+
|
|
63
|
+
def get_velocity_from_file(self) -> float:
|
|
64
|
+
return self._java_object.getVelocityFromFile()
|
|
65
|
+
|
|
66
|
+
def get_bscan_vertical_resolution(self) -> float:
|
|
67
|
+
return self._java_object.getBScanVerticalResolution()
|
|
68
|
+
|
|
69
|
+
def get_bscan_data(self, bscan_position: int) -> List[List[float]]:
|
|
70
|
+
return self._java_object.getBScanData(bscan_position)
|
|
71
|
+
|
|
72
|
+
def get_dscan_data(self, dscan_position: int) -> List[List[float]]:
|
|
73
|
+
return self._java_object.getDScanData(dscan_position)
|
|
74
|
+
|
|
75
|
+
def get_acquisition_type(self):
|
|
76
|
+
return self._java_object.getAcquisitionType()
|
|
77
|
+
|
|
78
|
+
def set_x_origin_position(self, x_offset: float) -> None:
|
|
79
|
+
return self._java_object.setXOriginPosition(x_offset)
|
|
80
|
+
|
|
81
|
+
def set_y_origin_position(self, y_offset: float) -> None:
|
|
82
|
+
return self._java_object.setYOriginPosition(y_offset)
|
|
83
|
+
|
|
84
|
+
def get_x_origin_position(self) -> float:
|
|
85
|
+
return self._java_object.getXOriginPosition()
|
|
86
|
+
|
|
87
|
+
def get_y_origin_position(self) -> float:
|
|
88
|
+
return self._java_object.getYOriginPosition()
|
|
89
|
+
|
|
90
|
+
def get_current_ascan(self) -> NIAScan:
|
|
91
|
+
return self._java_object.getCurrentAscan()
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
from typing import Any, List
|
|
2
|
+
from ...ndtkit_socket_connection import call_api_method
|
|
3
|
+
from .NICartographyFrame import NICartographyFrame
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class NICartographyFrameCScan(NICartographyFrame):
|
|
7
|
+
|
|
8
|
+
def get_identifier(self) -> int:
|
|
9
|
+
return self._java_object.getIdentifier()
|
|
10
|
+
|
|
11
|
+
def set_identifier(self, id: int) -> None:
|
|
12
|
+
return self._java_object.setIdentifier(id)
|
|
13
|
+
|
|
14
|
+
def get_x_resolution(self) -> float:
|
|
15
|
+
return self._java_object.getXResolution()
|
|
16
|
+
|
|
17
|
+
def get_y_resolution(self) -> float:
|
|
18
|
+
return self._java_object.getYResolution()
|
|
19
|
+
|
|
20
|
+
def get_resolution(self):
|
|
21
|
+
return self._java_object.getResolution()
|
|
22
|
+
|
|
23
|
+
def get_x_origin(self) -> float:
|
|
24
|
+
return self._java_object.getXOrigin()
|
|
25
|
+
|
|
26
|
+
def get_intrinsic_x_origin(self) -> float:
|
|
27
|
+
return self._java_object.getIntrinsicXOrigin()
|
|
28
|
+
|
|
29
|
+
def get_y_origin(self) -> float:
|
|
30
|
+
return self._java_object.getYOrigin()
|
|
31
|
+
|
|
32
|
+
def get_intrinsic_y_origin(self) -> float:
|
|
33
|
+
return self._java_object.getIntrinsicYOrigin()
|
|
34
|
+
|
|
35
|
+
def get_flat_data(self):
|
|
36
|
+
return self._java_object.getFlatData()
|
|
37
|
+
|
|
38
|
+
def get_data_bound(self, roi) -> List[List[float]]:
|
|
39
|
+
return self._java_object.getDataBound(roi)
|
|
40
|
+
|
|
41
|
+
def get_flat_data(self, roi) -> List[Any]:
|
|
42
|
+
return self._java_object.getFlatData(roi)
|
|
43
|
+
|
|
44
|
+
def add_processing_into_historic_list(self, text: str) -> None:
|
|
45
|
+
return self._java_object.addProcessingIntoHistoricList(text)
|
|
46
|
+
|
|
47
|
+
def get_file_path(self) -> str:
|
|
48
|
+
return self._java_object.getFilePath()
|
|
49
|
+
|
|
50
|
+
def set_automation_identifier(self, report_identifier: str) -> None:
|
|
51
|
+
return self._java_object.setAutomationIdentifier(report_identifier)
|
|
52
|
+
|
|
53
|
+
def get_automation_identifier(self) -> str:
|
|
54
|
+
return self._java_object.getAutomationIdentifier()
|
|
55
|
+
|
|
56
|
+
def get_unit(self):
|
|
57
|
+
return self._java_object.getUnit()
|
|
58
|
+
|
|
59
|
+
def get_palette(self):
|
|
60
|
+
return self._java_object.getPalette()
|
|
61
|
+
|
|
62
|
+
def load_palette(self, palette_pathname: str) -> None:
|
|
63
|
+
return self._java_object.loadPalette(palette_pathname)
|
|
64
|
+
|
|
65
|
+
def get_defects_detection(self):
|
|
66
|
+
return self._java_object.getDefectsDetection()
|
|
67
|
+
|
|
68
|
+
def zoom(self, x: float, y: float, width: float, height: float, ignore_ratio: bool) -> None:
|
|
69
|
+
return self._java_object.zoom(x, y, width, height, ignore_ratio)
|
|
70
|
+
|
|
71
|
+
def zoom(self, x: float, y: float, width: float, height: float) -> None:
|
|
72
|
+
return self._java_object.zoom(x, y, width, height)
|
|
73
|
+
|
|
74
|
+
def rotate(self, angle: int) -> None:
|
|
75
|
+
return self._java_object.rotate(angle)
|
|
76
|
+
|
|
77
|
+
def get_type(self):
|
|
78
|
+
return self._java_object.getType()
|
|
79
|
+
|
|
80
|
+
def horizontal_symmetry(self) -> None:
|
|
81
|
+
return self._java_object.horizontalSymmetry()
|
|
82
|
+
|
|
83
|
+
def vertical_symmetry(self) -> None:
|
|
84
|
+
return self._java_object.verticalSymmetry()
|
|
85
|
+
|
|
86
|
+
def get_amplitude_reference(self) -> float:
|
|
87
|
+
return self._java_object.getAmplitudeReference()
|
|
88
|
+
|
|
89
|
+
def get_velocity(self) -> float:
|
|
90
|
+
return self._java_object.getVelocity()
|
|
91
|
+
|
|
92
|
+
def get_ply_thickness(self) -> float:
|
|
93
|
+
return self._java_object.getPlyThickness()
|
|
94
|
+
|
|
95
|
+
def get_screenshot(self, representation_type, display_palette: bool, display_roi_layer: bool, display_mask_layer: bool, display_defect_layer: bool, display_dressing_layer: bool, display_rulers: bool, display_at_real_size: bool):
|
|
96
|
+
return self._java_object.getScreenshot(representation_type, display_palette, display_roi_layer, display_mask_layer, display_defect_layer, display_dressing_layer, display_rulers, display_at_real_size)
|
|
97
|
+
|
|
98
|
+
def set_file_path(self, filepath: str) -> None:
|
|
99
|
+
return self._java_object.setFilePath(filepath)
|
|
100
|
+
|
|
101
|
+
def set_unit_without_conversion(self, unit) -> None:
|
|
102
|
+
return self._java_object.setUnitWithoutConversion(unit)
|
|
103
|
+
|
|
104
|
+
def set_resolution_unit(self, unit) -> None:
|
|
105
|
+
return self._java_object.setResolutionUnit(unit)
|
|
106
|
+
|
|
107
|
+
def set_y_origin_position(self, position_in_mm: float) -> None:
|
|
108
|
+
return self._java_object.setYOriginPosition(position_in_mm)
|
|
109
|
+
|
|
110
|
+
def set_x_origin_position(self, position_in_mm: float) -> None:
|
|
111
|
+
return self._java_object.setXOriginPosition(position_in_mm)
|
|
112
|
+
|
|
113
|
+
def get_comment(self) -> str:
|
|
114
|
+
return self._java_object.getComment()
|
|
115
|
+
|
|
116
|
+
def set_comment(self, comment: str) -> None:
|
|
117
|
+
return self._java_object.setComment(comment)
|
|
118
|
+
|
|
119
|
+
def get_min_value(self) -> float:
|
|
120
|
+
return self._java_object.getMinValue()
|
|
121
|
+
|
|
122
|
+
def get_max_value(self) -> float:
|
|
123
|
+
return self._java_object.getMaxValue()
|
|
124
|
+
|
|
125
|
+
def get_metadata(self, metadata_id: str) -> Any:
|
|
126
|
+
return self._java_object.getMetadata(metadata_id)
|
|
127
|
+
|
|
128
|
+
def set_metadata(self, metadata_id: str, metadata: Any) -> None:
|
|
129
|
+
return self._java_object.setMetadata(metadata_id, metadata)
|
|
130
|
+
|
|
131
|
+
def apply_zoom(self, zoom_fit) -> None:
|
|
132
|
+
return self._java_object.applyZoom(zoom_fit)
|
|
133
|
+
|
|
134
|
+
def get_event_manager(self):
|
|
135
|
+
return self._java_object.getEventManager()
|
|
136
|
+
|
|
137
|
+
def is_still_displayed(self) -> bool:
|
|
138
|
+
return self._java_object.isStillDisplayed()
|
|
139
|
+
|
|
140
|
+
def request_focus(self) -> None:
|
|
141
|
+
return self._java_object.requestFocus()
|
|
142
|
+
|
|
143
|
+
def get_zoom_factor_x(self) -> float:
|
|
144
|
+
return self._java_object.getZoomFactorX()
|
|
145
|
+
|
|
146
|
+
def get_zoom_factor_y(self) -> float:
|
|
147
|
+
return self._java_object.getZoomFactorY()
|
|
148
|
+
|
|
149
|
+
def get_dynamic_layer_manager(self):
|
|
150
|
+
return self._java_object.getDynamicLayerManager()
|
|
151
|
+
|
|
152
|
+
def refresh_layers(self) -> None:
|
|
153
|
+
return self._java_object.refreshLayers()
|
|
154
|
+
|
|
155
|
+
def duplicate(self):
|
|
156
|
+
return self._java_object.duplicate()
|
|
157
|
+
|
|
158
|
+
def json_of_scan_reader_parameter(self) -> str:
|
|
159
|
+
return self._java_object.jsonOfScanReaderParameter()
|
|
160
|
+
|
|
161
|
+
def set_auto_update_data(self, auto_update_data: bool) -> None:
|
|
162
|
+
return self._java_object.setAutoUpdateData(auto_update_data)
|
|
163
|
+
|
|
164
|
+
def update_data(self, update_palette: bool) -> None:
|
|
165
|
+
return self._java_object.updateData(update_palette)
|
|
166
|
+
|
|
167
|
+
def get_projection_cuboid(self):
|
|
168
|
+
return self._java_object.getProjectionCuboid()
|
|
169
|
+
|
|
170
|
+
def get_position3d(self, real_x: float, real_y: float):
|
|
171
|
+
return self._java_object.getPosition3d(real_x, real_y)
|
|
172
|
+
|
|
173
|
+
def get_horizontal_echodynamic_curve(self):
|
|
174
|
+
return self._java_object.getHorizontalEchodynamicCurve()
|
|
175
|
+
|
|
176
|
+
def get_vertical_echodynamic_curve(self):
|
|
177
|
+
return self._java_object.getVerticalEchodynamicCurve()
|
|
178
|
+
|
|
179
|
+
def get_data(self, roi=None) -> list[list[float]]:
|
|
180
|
+
if (roi):
|
|
181
|
+
return self._java_object.get_data(roi)
|
|
182
|
+
|
|
183
|
+
parameters = [
|
|
184
|
+
{
|
|
185
|
+
"type": "agi.ndtkit.api.model.frame.NICartographyFrameCScan",
|
|
186
|
+
"value": self.get_uuid(),
|
|
187
|
+
}
|
|
188
|
+
]
|
|
189
|
+
return call_api_method("agi.ndtkit.api.model.frame", "NICartographyFrameCScan", "getData", parameters)
|
|
190
|
+
|
|
191
|
+
def set_data(self, data: list[list[float]]):
|
|
192
|
+
parameters = [
|
|
193
|
+
{
|
|
194
|
+
"type": "agi.ndtkit.api.model.frame.NICartographyFrameCScan",
|
|
195
|
+
"value": self.get_uuid(),
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"type": "float[][]",
|
|
199
|
+
"value": str(data)
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
call_api_method("agi.ndtkit.api.model.frame", "NICartographyFrameCScan", "setData", parameters)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from py4j.java_gateway import JavaObject
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class NIAScan:
|
|
5
|
+
"""Base class for NIAScan, generated from Java API."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, java_object: JavaObject, json_model=None):
|
|
8
|
+
self._java_object = java_object
|
|
9
|
+
self.json_model = json_model
|
|
10
|
+
|
|
11
|
+
def get_all_tof_values_from_this_ascan_and_sub_ascans(self) -> list[float]:
|
|
12
|
+
return self._java_object.getAllTofValuesFromThisAScanAndSubAscans()
|
|
13
|
+
|
|
14
|
+
def get_all_amp_values_from_this_ascan_and_sub_ascans(self) -> list[float]:
|
|
15
|
+
return self._java_object.getAllAmpValuesFromThisAScanAndSubAscans()
|
|
16
|
+
|
|
17
|
+
def get_amp_values(self) -> list[float]:
|
|
18
|
+
if self.json_model and "amp" in self.json_model:
|
|
19
|
+
return self.json_model["amp"]
|
|
20
|
+
return self._java_object.getAmpValues()
|
|
21
|
+
|
|
22
|
+
def get_tof_values(self) -> list[float]:
|
|
23
|
+
return self._java_object.getTofValues()
|
|
24
|
+
|
|
25
|
+
def get_sub_ascans(self):
|
|
26
|
+
return self._java_object.getSubAscans()
|
|
27
|
+
|
|
28
|
+
def get_column(self) -> int:
|
|
29
|
+
return self._java_object.getColumn()
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import socket
|
|
2
|
+
import struct # Required to pack numbers into bytes
|
|
3
|
+
import json # Required to work with JSON data
|
|
4
|
+
from .config import SERVER_HOST, SERVER_PORT, MESSAGE_TYPE, SOCKET_SERVER_COMMAND, SERVER_CONNECTION_TIMEOUT
|
|
5
|
+
from py4j.java_gateway import JavaGateway
|
|
6
|
+
import time
|
|
7
|
+
import subprocess
|
|
8
|
+
|
|
9
|
+
gateway = JavaGateway()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def check_server() -> socket.socket | None:
|
|
13
|
+
"""
|
|
14
|
+
Creates a socket and tries to connect to the server.
|
|
15
|
+
Returns the connected socket object on success, or None on failure.
|
|
16
|
+
"""
|
|
17
|
+
try:
|
|
18
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
19
|
+
s.settimeout(2) # Set a connection timeout of 2 seconds
|
|
20
|
+
# 2. Attempt to connect
|
|
21
|
+
s.connect((SERVER_HOST, SERVER_PORT))
|
|
22
|
+
# 3. Return the valid, open socket
|
|
23
|
+
return s
|
|
24
|
+
except (ConnectionRefusedError, socket.timeout):
|
|
25
|
+
# The connection failed, close the created socket and return None
|
|
26
|
+
s.close()
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def send_custom_protocol_message(json_data):
|
|
31
|
+
"""
|
|
32
|
+
Builds and sends a message according to the custom Netty protocol.
|
|
33
|
+
Format: [4-byte length][1-byte type][json payload]
|
|
34
|
+
"""
|
|
35
|
+
try:
|
|
36
|
+
# 1. Convert the Python dictionary to a JSON string, then encode it into bytes.
|
|
37
|
+
# Using ensure_ascii=False is good practice for non-latin characters.
|
|
38
|
+
json_payload_bytes = json.dumps(json_data, ensure_ascii=False).encode('utf-8')
|
|
39
|
+
|
|
40
|
+
# 2. Calculate the "messageLength" for the header.
|
|
41
|
+
# This is the length of the type (1 byte) + the length of the JSON payload.
|
|
42
|
+
header_length = 1 + len(json_payload_bytes)
|
|
43
|
+
|
|
44
|
+
# 3. Pack the header fields into bytes.
|
|
45
|
+
# '>' = Big-Endian (Network Byte Order, standard for Java)
|
|
46
|
+
# 'I' = Unsigned Integer (4 bytes)
|
|
47
|
+
# 'B' = Unsigned Char (1 byte)
|
|
48
|
+
length_prefix = struct.pack('>I', header_length)
|
|
49
|
+
type_prefix = struct.pack('>B', MESSAGE_TYPE)
|
|
50
|
+
|
|
51
|
+
# 4. Assemble the final message by concatenating all parts in the correct order.
|
|
52
|
+
payload_to_send = length_prefix + type_prefix + json_payload_bytes
|
|
53
|
+
|
|
54
|
+
# 5. Connect and send the message.
|
|
55
|
+
start_time = time.time()
|
|
56
|
+
# Loop until the timeout is reached
|
|
57
|
+
is_ndtkit_launch_command_sent = False
|
|
58
|
+
while time.time() - start_time < SERVER_CONNECTION_TIMEOUT:
|
|
59
|
+
client_socket = check_server()
|
|
60
|
+
if client_socket:
|
|
61
|
+
break # Exit the loop since the server was found
|
|
62
|
+
else:
|
|
63
|
+
if not is_ndtkit_launch_command_sent:
|
|
64
|
+
is_ndtkit_launch_command_sent = True
|
|
65
|
+
if SOCKET_SERVER_COMMAND.endswith(".bat"):
|
|
66
|
+
subprocess.Popen(SOCKET_SERVER_COMMAND)
|
|
67
|
+
else:
|
|
68
|
+
subprocess.call(SOCKET_SERVER_COMMAND)
|
|
69
|
+
time.sleep(5)
|
|
70
|
+
|
|
71
|
+
if not client_socket:
|
|
72
|
+
raise ConnectionError("Impossible to reach the socket server")
|
|
73
|
+
|
|
74
|
+
client_socket.settimeout(None) # Remove the timeout
|
|
75
|
+
client_socket.sendall(payload_to_send)
|
|
76
|
+
|
|
77
|
+
# --- Receive the response ---
|
|
78
|
+
|
|
79
|
+
# 1. Read the 4-byte total length prefix
|
|
80
|
+
response_len_bytes = client_socket.recv(4)
|
|
81
|
+
if not response_len_bytes:
|
|
82
|
+
print("<-- Server closed connection without response.")
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
total_len = struct.unpack('>I', response_len_bytes)[0]
|
|
86
|
+
|
|
87
|
+
# 2. Read the 1-byte message type
|
|
88
|
+
response_type_byte = client_socket.recv(1)
|
|
89
|
+
if not response_type_byte:
|
|
90
|
+
raise ConnectionError("Connection broken after reading length.")
|
|
91
|
+
|
|
92
|
+
# 3. Calculate the length of the JSON payload
|
|
93
|
+
json_len = total_len - 1
|
|
94
|
+
|
|
95
|
+
# 4. Read the JSON payload
|
|
96
|
+
response_payload = b''
|
|
97
|
+
if json_len > 0:
|
|
98
|
+
while len(response_payload) < json_len:
|
|
99
|
+
chunk = client_socket.recv(json_len - len(response_payload))
|
|
100
|
+
if not chunk:
|
|
101
|
+
raise ConnectionError("Socket connection broken before full response was received.")
|
|
102
|
+
response_payload += chunk
|
|
103
|
+
|
|
104
|
+
# 5. Decode and parse the JSON
|
|
105
|
+
return json.loads(response_payload.decode('utf-8'))
|
|
106
|
+
|
|
107
|
+
except Exception as e:
|
|
108
|
+
print(f"An error occurred: {e}")
|
|
109
|
+
finally:
|
|
110
|
+
if client_socket:
|
|
111
|
+
client_socket.close()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def call_api_method(package_name, class_name, method_name, parameters):
|
|
115
|
+
json_command = {
|
|
116
|
+
"packageName": package_name,
|
|
117
|
+
"className": class_name,
|
|
118
|
+
"methodName": method_name,
|
|
119
|
+
"parameters": parameters
|
|
120
|
+
}
|
|
121
|
+
return send_custom_protocol_message(json_command)
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ndtkit_api
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: This Python API provides a high-level interface to interact with the NDTkit desktop application. It allows developers to script and automate NDTkit features remotely, leveraging the full power of the underlying Java application through a simple and pythonic library.
|
|
5
|
+
Author-email: Cédric BERTRAND <cedric.bertrand@testia.com>
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Operating System :: OS Independent
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Topic :: Software Development
|
|
10
|
+
Requires-Python: >=3.11.9
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: requests
|
|
14
|
+
Requires-Dist: py4j
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# NDTkit Python API
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+

|
|
21
|
+

|
|
22
|
+
|
|
23
|
+
This Python API provides a high-level interface to interact with the NDTkit desktop application. It allows developers to script and automate NDTkit features remotely, leveraging the full power of the underlying Java application through a simple and pythonic library.
|
|
24
|
+
|
|
25
|
+
## How it Works ⚙️
|
|
26
|
+
|
|
27
|
+
The API acts as a client that communicates with a dedicated socket server running within the NDTkit Java application.
|
|
28
|
+
|
|
29
|
+
1. **Python Call**: A function is called in Python (e.g., `NDTKitCScanInterface.open_cscan(...)`).
|
|
30
|
+
2. **JSON Serialization**: The call (class name, method name, parameters) is serialized into a JSON object.
|
|
31
|
+
3. **Socket Communication**: The JSON payload is sent over a TCP socket to the Java server.
|
|
32
|
+
4. **Java Execution**: The server deserializes the request, invokes the corresponding Java API method using reflection, and waits for the result.
|
|
33
|
+
5. **Return Value**: The result from the Java method is serialized back to JSON and sent to the Python client, which deserializes it into a Python object (like a `NICartographyFrameCScan` instance).
|
|
34
|
+
|
|
35
|
+
## Prerequisites
|
|
36
|
+
|
|
37
|
+
Before using this API, you must have:
|
|
38
|
+
|
|
39
|
+
1. **Python 3.9+** installed.
|
|
40
|
+
2. The **NDTkit desktop application** installed.
|
|
41
|
+
3. The Java socket server for the API must be running. The Python API can automatically launch it if configured correctly.
|
|
42
|
+
|
|
43
|
+
## 🚀 Installation & Configuration
|
|
44
|
+
|
|
45
|
+
1. **Clone the repository** (or add it to your project). This library is not on PyPI.
|
|
46
|
+
|
|
47
|
+
2. **Configure the connection**: At the root of the `apipython` package, you will find a `config` directory. You need to edit the `config.ini` file.
|
|
48
|
+
|
|
49
|
+
```ini
|
|
50
|
+
[Parameters]
|
|
51
|
+
SERVER_HOST = 127.0.0.1
|
|
52
|
+
SERVER_PORT = 32146
|
|
53
|
+
MESSAGE_TYPE = 10
|
|
54
|
+
|
|
55
|
+
# IMPORTANT: Update this path to point to your serverMode script
|
|
56
|
+
SOCKET_SERVER_COMMAND = <NDTKIT_INSTALL_FOLDER>/NDTkit.vbs # Alternative: <NDTKIT_INSTALL_FOLDER>/modules/serverMode/serverMode.bat
|
|
57
|
+
|
|
58
|
+
SERVER_CONNECTION_TIMEOUT = 120
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- **`SERVER_HOST`**: The IP address of the machine running NDTkit. Use `127.0.0.1` if it's on the same machine.
|
|
62
|
+
- **`SERVER_PORT`**: The port for the socket communication. This must match the port the NDTkit server is listening on.
|
|
63
|
+
- **`SOCKET_SERVER_COMMAND`**: **This is the most critical setting.** It's the command used to launch the Java socket server. The Python API will execute this command if it cannot connect to the server.
|
|
64
|
+
|
|
65
|
+
## Usage Example
|
|
66
|
+
|
|
67
|
+
Here is a simple example showing how to open a C-Scan, modify a pixel, and save it to a new file.
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
# examples.py
|
|
71
|
+
|
|
72
|
+
import NDTKitCScanInterface
|
|
73
|
+
from model.frame.NICartographyFrameCScan import NICartographyFrameCScan
|
|
74
|
+
|
|
75
|
+
def change_pixel_value(input_file: str, output_file: str):
|
|
76
|
+
"""
|
|
77
|
+
Opens a C-Scan, modifies the value of the first pixel, saves it,
|
|
78
|
+
and re-opens it for display.
|
|
79
|
+
"""
|
|
80
|
+
print(f"Opening C-Scan: {input_file}")
|
|
81
|
+
# The server is launched automatically if not running
|
|
82
|
+
cscan = NDTKitCScanInterface.openCScan(input_file)
|
|
83
|
+
|
|
84
|
+
if not cscan:
|
|
85
|
+
print("Failed to open C-Scan.")
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
print(f"C-Scan opened successfully with UUID: {cscan.get_uuid()}")
|
|
89
|
+
|
|
90
|
+
print("Retrieving data matrix...")
|
|
91
|
+
data = cscan.get_data()
|
|
92
|
+
print(f"Original value at [0][0]: {data[0][0]}")
|
|
93
|
+
|
|
94
|
+
# Modify the data
|
|
95
|
+
data[0][0] = 12.0
|
|
96
|
+
print(f"Setting new value at [0][0] to 12.0")
|
|
97
|
+
cscan.set_data(data)
|
|
98
|
+
|
|
99
|
+
print(f"Saving modified C-Scan to: {output_file}")
|
|
100
|
+
NDTKitCScanInterface.saveCscan(cscan, output_file)
|
|
101
|
+
|
|
102
|
+
print("Re-opening the saved C-Scan to verify.")
|
|
103
|
+
NDTKitCScanInterface.openCScan(output_file, displayResult=True)
|
|
104
|
+
print("Done.")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
if __name__ == "__main__":
|
|
108
|
+
# Make sure to use paths that are valid on your system
|
|
109
|
+
input_cscan_path = "D:/Data/NKC/dd_carto.nkc"
|
|
110
|
+
output_cscan_path = "D:/tmp/modified_cscan.nkc"
|
|
111
|
+
change_pixel_value(input_cscan_path, output_cscan_path)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## A class/method available in the Java API which is not appearing in this Python API
|
|
115
|
+
|
|
116
|
+
It can reachable anyway, it's just the auto-completion of your IDE that is not working. Actually this Python API is using [py4j](https://www.py4j.org/) that allows to access any Java API function.
|
|
117
|
+
Here's an example on how to access to `agi.ndtkit.api.NDTKitCScanInterface.openCScan(String)` without using this wrapper project:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from ndtkit_socket_connection import gateway
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
gateway.jvm.agi.ndtkit.api.NDTKitCScanInterface.openCScan("path/to/my/cscan")
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
So please refers to the NDTkit Javadoc to have an exhaustive look at all the available actions and call them using this solution if they are not available directly in this Python API.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
ndtkit_api/NDTKitAScanInterface.py
|
|
5
|
+
ndtkit_api/NDTKitCScanInterface.py
|
|
6
|
+
ndtkit_api/ndtkit_socket_connection.py
|
|
7
|
+
ndtkit_api.egg-info/PKG-INFO
|
|
8
|
+
ndtkit_api.egg-info/SOURCES.txt
|
|
9
|
+
ndtkit_api.egg-info/dependency_links.txt
|
|
10
|
+
ndtkit_api.egg-info/requires.txt
|
|
11
|
+
ndtkit_api.egg-info/top_level.txt
|
|
12
|
+
ndtkit_api/config/__init__.py
|
|
13
|
+
ndtkit_api/model/ascan/NIAScanGate.py
|
|
14
|
+
ndtkit_api/model/frame/NICartographyFrame.py
|
|
15
|
+
ndtkit_api/model/frame/NICartographyFrameAScan.py
|
|
16
|
+
ndtkit_api/model/frame/NICartographyFrameCScan.py
|
|
17
|
+
ndtkit_api/model/scans/ascan/NIAScan.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ndtkit_api
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ndtkit_api"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Cédric BERTRAND", email="cedric.bertrand@testia.com" },
|
|
10
|
+
]
|
|
11
|
+
description = "This Python API provides a high-level interface to interact with the NDTkit desktop application. It allows developers to script and automate NDTkit features remotely, leveraging the full power of the underlying Java application through a simple and pythonic library."
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.11.9"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Operating System :: OS Independent",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Topic :: Software Development",
|
|
19
|
+
]
|
|
20
|
+
dependencies = [
|
|
21
|
+
"requests",
|
|
22
|
+
"py4j"
|
|
23
|
+
]
|