# 脚本

Script 节点允许用户在设备上运行自定义 Python 脚本。由于计算资源限制，Script 节点不应用于繁重计算（如图像处理/计算机视觉），而应用于管理流水线的流程（业务逻辑）。典型使用场景包括：控制
[ImageManip](https://docs.luxonis.com/software-v3/depthai/depthai-components/nodes/image_manip.md)、[ColorCamera](https://docs.luxonis.com/software-v3/depthai/depthai-components/nodes/color_camera.md)、[SpatialLocationCalculator](https://docs.luxonis.com/software-v3/depthai/depthai-components/nodes/spatial_location_calculator.md)
等节点，解码 [NeuralNetwork](https://docs.luxonis.com/software-v3/depthai/depthai-components/nodes/neural_network.md) 的结果，或与 GPIO
接口进行交互。

## 如何放置

#### Python

```python
pipeline = dai.Pipeline()
script = pipeline.create(dai.node.Script)
```

#### C++

```cpp
dai::Pipeline pipeline;
auto script = pipeline.create<dai::node::Script>();
```

## 输入和输出

用户可以根据需要定义任意数量的输入和输出。输入和输出可以是任何
[components_messages](https://docs.luxonis.com/software-v3/depthai/depthai-components/messages/message_group.md) 类型。

## 使用

#### Python

```python
script = pipeline.create(dai.node.Script)
script.setScript("""
    import time
    import marshal
    num = 123
    node.warn(f"Number {num}") # 打印到主机
    x = [1, "Hello", {"Foo": "Bar"}]
    x_serial = marshal.dumps(x)
    b = Buffer(len(x_serial))
    while True:
        time.sleep(1)
        b.setData(x_serial)
        node.io['out'].send(b)
""")
script.outputs['out'].link(xout.input)

# ...
# 初始化设备后，启用日志级别
device.setLogLevel(dai.LogLevel.WARN)
device.setLogOutputLevel(dai.LogLevel.WARN)
```

#### C++

```cpp
auto script = pipeline.create<dai::node::Script>();
script->setScript(R"(
    import time
    import marshal
    num = 123
    node.warn(f"Number {num}") # 打印到主机
    x = [1, "Hello", {"Foo": "Bar"}]
    x_serial = marshal.dumps(x)
    b = Buffer(len(x_serial))
    while True:
        time.sleep(1)
        b.setData(x_serial)
        node.io['out'].send(b)
)");
script->outputs["out"].link(xout->input);

// ...
// 初始化设备后，启用日志级别
device.setLogLevel(dai::LogLevel.WARN);
device.setLogOutputLevel(dai::LogLevel.WARN);
```

## 与 GPIO 接口交互

在 Script 节点中，您可以通过 GPIO 模块与 VPU 的 GPIO 接口交互。目前支持的功能如下：

```python
# 模块
import GPIO

# 通用
GPIO.setup(gpio, dir, pud, exclusive)
GPIO.release(gpio)
GPIO.write(gpio, value)
GPIO.read(gpio)

# 中断
GPIO.waitInterruptEvent(gpio = -1) # 阻塞直到任何中断或指定 gpio 的中断触发。忽略带回调的中断
GPIO.hasInterruptEvent(gpio = -1) # 返回是否在任何或指定 gpio 上发生了中断。忽略带回调的中断
GPIO.setInterrupt(gpio, edge, priority, callback = None) # 为指定引脚添加中断
GPIO.clearInterrupt(gpio) # 清除指定引脚的中断

# PWM
GPIO.setPwm(gpio, highCount, lowCount, repeat=0) # repeat == 0 表示无限
GPIO.enablePwm(gpio, enable)

# 枚举
GPIO.Direction: GPIO.IN, GPIO.OUT
GPIO.State: GPIO.LOW, GPIO.HIGH
GPIO.PullDownUp: GPIO.PULL_NONE, GPIO.PULL_DOWN, GPIO.PULL_UP
GPIO.Edge: GPIO.RISING, GPIO.FALLING, GPIO.LEVEL_HIGH, GPIO.LEVEL_LOW
```

以下是从主机在 Script 节点内切换 GPIO 引脚 40 的示例。在
[OAK-SoM-Pro](https://shop71313603.taobao.com/?spm=pc_detail.30350276.shop_block.dshopinfo.27a17dd635FNDA) 上， GPIO 40 驱动两个四线摄像头的
FSYNC 信号，我们正是出于此原因使用了下面的代码。

```python
import GPIO
MX_PIN = 40

ret = GPIO.setup(MX_PIN, GPIO.OUT, GPIO.PULL_DOWN)
toggleVal = True

while True:
  data = node.io['in'].get()  # 等待来自主机计算机的消息

  node.warn('GPIO 切换: ' + str(toggleVal))
  toggleVal = not toggleVal
  ret = GPIO.write(MX_PIN, toggleVal)  # 切换 GPIO
```

## 时间同步

Script 节点可以访问设备（内部）时钟和同步后的主机时钟。主机时钟与设备时钟同步，精度在 1σ 下低于 2.5ms。详见
[主机时钟同步](https://docs.luxonis.com/software-v3/depthai/depthai-components/device.md)。

```python
import time
interval = 60
ctrl = CameraControl()
ctrl.setCaptureStill(True)
previous = 0
while True:
    time.sleep(0.001)

    tnow_full = Clock.nowHost() # 与主机同步的时钟
    # Clock.now() -> 内部/设备时钟
    # Clock.offsetToHost() -> 内部/设备时钟与主机时钟之间的偏移

    now = tnow_full.seconds
    if now % interval == 0 and now != previous:
        previous = now
        node.warn(f'{tnow_full}')
        node.io['out'].send(ctrl)
```

## 使用 DepthAI [消息](https://docs.luxonis.com/software-v3/depthai/depthai-components/messages.md)

depthai 模块已被隐式导入到脚本节点中。您可以创建新的 depthai 消息并为其分配数据，例如：

```python
buf = Buffer(100) # 为 Buffer 消息分配 100 字节

# 创建 CameraControl 消息，设置手动对焦
control = CameraControl()
control.setManualFocus(100)

imgFrame = ImgFrame(300*300*3) # 包含 300x300x3 字节的缓冲区
```

## 可用的模块和库

#### RVC2

可用模块

```text
"posix", "errno", "pwd", "_sre", "_codecs", "_weakref", "_functools", "_operator",
"_collections", "_abc", "itertools", "atexit", "_stat", "time", "_datetime", "math",
"_thread", "_io", "_symtable", "marshal", "_ast", "gc", "_warnings", "_string", "_struct"
```

LEON_CSS 可用模块：

```text
"binascii", "_random", "_socket", "_md5", "_sha1", "_sha256", "_sha512", "select",
"array", "unicodedata"
```

库

```text
"__main__", "_collections_abc", "_frozen_importlib", "_frozen_importlib_external",
"_sitebuiltins", "abc", "codecs", "datetime", "encodings", "encodings.aliases",
"encodings.ascii", "encodings.latin_1", "encodings.mbcs", "encodings.utf_8", "genericpath",
"io", "os", "posixpath", "site", "stat", "threading", "types", "struct", "copyreg",
"reprlib", "operator", "keyword", "heapq", "collections", "functools", "sre_constants",
"sre_parse", "sre_compile", "enum", "re", "json", "json.decoder", "json.encoder",
"json.scanner", "textwrap"
```

LEON_CSS 库：

```text
"http", "http.client", "http.server", "html", "mimetypes", "copy", "shutil", "fnmatch",
"socketserver", "contextlib", "email", "email._encoded_words", "email._header_value_parser",
"email._parseaddr", "email._policybase", "email.base64mime", "email.charset",
"email.contentmanager",  "email.encoders", "email.errors", "email.feedparser",
"email.generator", "email.header", "email.headerregistry", "email.iterators", "email.message",
"email.parser", "email.policy", "email.quoprimime", "email.utils", "string", "base64",
"quopri", "random", "warnings", "bisect", "hashlib", "logging", "traceback", "linecache",
"socket", "token", "tokenize", "weakref", "_weakrefset", "collections.abc", "selectors",
"urllib", "urllib.parse", "calendar", "locale", "uu", "encodings.idna", "stringprep"
```

模块与库的区别在于：模块是预先编译的带有Python绑定的C源码，而库是打包成库并预先编译为Python字节码（在加载到我们的固件之前）的Python源代码。 LEON_CSS上可用的网络/协议模块/库 只能在 [OAK
POE设备](https://docs.luxonis.com/hardware.md#poe-designs)上使用。 您可以指定脚本在哪个处理器上运行，例如对于LEON_CSS：

```python
script = pipeline.create(dai.node.Script)
script.setProcessor(dai.ProcessorType.LEON_CSS)
```

#### RVC4

OAK4上的脚本节点使用基础Python环境，这意味着基础Python环境中可用的所有模块和库在脚本节点中也可用。 要添加额外的模块或库，可以在基础Python环境中使用pip命令。

## 功能示例

 * [Script simple](https://docs.luxonis.com/software-v3/depthai/examples/script/script_simple.md) - 使用Script节点发送和接收消息的简单示例。
 * [Script switch cameras](https://docs.luxonis.com/software-v3/depthai/examples/script/script_switch_all_cameras.md) -
   使用Script节点在多个摄像头之间切换的示例。

## 参考

### dai::node::Script

Kind: class

#### InputMap inputs

Kind: variable

Inputs to Script node. Can be accessed using subscript operator (Eg: inputs['in1']) By default inputs are set to blocking with
queue size 8

#### OutputMap outputs

Kind: variable

Outputs from Script node. Can be accessed subscript operator (Eg: outputs['out1'])

#### void setScriptPath(const std::filesystem::path & path, const std::string & name)

Kind: function

Specify local filesystem path to load the script parameters: path: Filesystem path to load the script; name: Optionally set a name
of this script, otherwise the name defaults to the path

#### void setScript(const std::string & script, const std::string & name)

Kind: function

Sets script data to be interpreted parameters: script: Script string to be interpreted; name: Optionally set a name of this script

#### void setScript(const std::vector< std::uint8_t > & data, const std::string & name)

Kind: function

Sets script data to be interpreted parameters: data: Binary data that represents the script to be interpreted; name: Optionally
set a name of this script

#### std::filesystem::path getScriptPath()

Kind: function

Get filesystem path from where script was loaded.

return: std::filesystem::path from where script was loaded, otherwise returns empty path

#### std::string getScriptName()

Kind: function

Get the script name in utf-8.

When name set with setScript() or setScriptPath() , returns that name. When script loaded with setScriptPath() with name not
provided, returns the utf-8 string of that path. Otherwise, returns "<script>" return: std::string of script name in utf-8

#### void setProcessor(ProcessorType type)

Kind: function

Set on which processor the script should run parameters: type: Processor type - Leon CSS or Leon MSS

#### ProcessorType getProcessor()

Kind: function

Get on which processor the script should run return: Processor type - Leon CSS or Leon MSS

#### void buildInternal()

Kind: function

Function called from within the

#### void buildStage1()

Kind: function

Build stages;.

#### DeviceNodeCRTP()

Kind: function

#### DeviceNodeCRTP(const std::shared_ptr< Device > & device)

Kind: function

#### DeviceNodeCRTP(std::unique_ptr< Properties > props)

Kind: function

#### DeviceNodeCRTP(std::unique_ptr< Properties > props, bool confMode)

Kind: function

#### DeviceNodeCRTP(const std::shared_ptr< Device > & device, std::unique_ptr< Properties > props, bool confMode)

Kind: function

### 需要帮助？

请前往 [OAKChina 官网](https://www.oakchina.cn/) 获取技术支持或解答您的任何疑问。
