Metadata-Version: 2.4
Name: PKIPC
Version: 3.0.0
Summary: Cross-platform SDK for industrial software and C++/Python solvers
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: grpcio<2,>=1.81.1
Requires-Dist: protobuf<7,>=6.33.5
Provides-Extra: test
Requires-Dist: pytest>=8.2; extra == "test"
Requires-Dist: ruff>=0.12; extra == "test"
Requires-Dist: cryptography>=43; extra == "test"
Requires-Dist: requests<3,>=2.32; extra == "test"
Provides-Extra: build
Requires-Dist: grpcio-tools==1.81.1; extra == "build"
Requires-Dist: build>=1.2; extra == "build"
Provides-Extra: release
Requires-Dist: twine<8,>=6.2; extra == "release"
Requires-Dist: requests<3,>=2.32; extra == "release"

# PKIPC SDK 3 — Python 求解器与软件端

面向工业软件调用外部求解器。C++/Python 可以任意组合；本地软件端启动并管理求解器子进程，远程模式连接已部署的求解器。通信使用一条 gRPC 双向流，算法作者只接触配置与自由事件。

```sh
python -m pip install .
```

## 求解器

```python
import threading
from pkipc import Solver

solver = Solver()
stopped = threading.Event()


@solver.on("math.add")
def add(data):
    return {"result": data["a"] + data["b"]}


@solver.on("stop")
def stop(_):
    stopped.set()


try:
    config = solver.start()
    solver.send("started", config)
    while not stopped.wait(0.02) and not solver.stop_requested():
        pass  # 保留你自己的计算循环
finally:
    solver.close()
```

求解器由 SDK Client 启动时自动取得连接参数；算法作者不需要配置端口或处理请求匹配。需要请求软件提供数据时，在计算线程调用 `solver.request("material.lookup", data)`。

## 软件端

```python
import sys
from pkipc import Client

client = Client(sys.executable, args=["solver.py"], config={"threads": 4})
client.on("started", lambda data: print(data))
try:
    client.start()
    result = client.request("math.add", {"a": 20, "b": 22})
    client.send("stop")
    print(client.wait(timeout=5))
finally:
    client.close()
```

启动 C++ 求解器时，把 executable 换成对应平台的可执行文件即可。Client 还支持 working_directory、environment、start_timeout、stop_timeout。args 是参数数组，不经过 shell。close 先断开会话，给直接子进程退出时间，超时终止并回收。with Client(...) 会自动 start/close；需要预先注册回调时使用上面的显式写法。

## 自由事件、线程和错误

两端都提供 on/send/request/request_async。事件名使用 ASCII 字母、数字、点、下划线、连字符或斜杠；`pkipc.` 保留给 SDK。数据为 JSON，包含 null、bool、有限浮点、64 位整数范围、字符串、list、dict。Python tuple、bytes、自定义对象和超范围整数需业务显式转换。

每个会话串行执行事件回调；回调线程与计算主线程并行，使用 Event/Queue/Lock 交换状态。长计算保留在计算线程，避免阻塞暂停等命令回调。同步 request 不允许在事件 handler 内调用；request_async 返回 concurrent.futures.Future。Future 的完成回调在 SDK 线程执行，必须快速返回，不能阻塞等待其他 SDK 工作。

所有 SDK 错误使用 Error，其 code 为 ErrorCode，remote 表示对端业务错误。两种语言错误码和默认消息一致，普通 handler 异常映射 HANDLER_ERROR；显式抛出 Error 保留错误码。单向 handler 错误由 on_error 接收。业务暂停、继续、日志与修改参数都使用普通事件，没有固定求解流程。

send 仅保证本地入队；需要确认时使用 request。超时不代表远端没有执行，SDK 不自动重放请求。close 取消等待中的请求并丢弃未发送消息；计算循环应检查 stop_requested，用户 handler 应能结束。wait 返回子进程退出码，超时返回 None；remote Client 没有本地 PID。

## 远程与 TLS

求解器使用 `Solver(listen="host:port", token=..., tls=TLS(certificate=cert_pem, private_key=key_pem))`；软件使用 `Client(endpoint="host:port", token=..., tls=TLS(roots=ca_pem), config=...)`。随后事件接口与本地相同。TLS 使用 PEM bytes，支持客户端证书和 require_client_certificate。

非回环连接默认要求 TLS；受控测试可显式 allow_insecure=True。远程进程由部署系统启动和管理，SDK 管理会话。仓库 examples/solver.py 也支持通过 PKIPC_LISTEN、PKIPC_TOKEN、PKIPC_TLS_CERT、PKIPC_TLS_KEY 配置部署，算法代码不需要改变。

## 开发与一致性验证

```sh
python -m pip install -e ".[test,build]"
python -m pytest -q
python -m ruff check .
python -m build
```

设置 PKIPC_CPP_SOLVER / PKIPC_CPP_CLIENT 为 C++ 构建产物路径，启用四种语言组合与 TLS 远程交叉测试。未提供二进制时这些用例会明确显示 skipped。C++ 仓库 proto 是权威协议，`python tools/sync_protocol.py --source /path/to/PKIPC --check` 检查副本一致性；不加 --check 时同步并生成 Python stub。

两个源码仓库在本机时，可以一条命令构建 C++、检查协议副本并运行完整互操作测试：`python tools/verify_sdk.py --cpp-source /path/to/PKIPC --cpp-build /short/build/path`。已有构建产物时加 `--skip-build`；该脚本要求 C++ 示例存在，避免误将缺少原生测试当作完整验证。

SDK 3 删除了旧 Server 类和旧运行时，不兼容协议 2。pkipc._session 和 pkipc._listener 是内部实现，不是公开通信框架 API。详细协议和共享数值用例随包发布。支持 Python 3.11+；具体操作系统/依赖组合以 CI 实测为准。

## 发布到 PyPI

更新 pyproject.toml 与 pkipc.__version__，提交源码，并准备好同版本的 C++ 构建后：

```sh
python -m pip install -e ".[test,build,release]"
python tools/release.py prepare --cpp-source /path/to/PKIPC --cpp-build /short/build/path
python tools/release.py publish --dry-run
python tools/release.py publish
```

prepare 运行完整互操作测试，从 Git 提交的干净快照构建 wheel/sdist，执行严格元数据检查与安装后的求解器冒烟测试，保存 SHA-256 清单到 dist/版本号。publish 只上传清单中的文件，并核对 PyPI 返回的校验值；同版本的相同文件可续传，不同内容会明确拒绝。默认读取现有环境变量 `PYPI_API`，也兼容 Twine 的 `TWINE_PASSWORD` 或 .pypirc 配置。Token 不写进脚本或命令参数。PyPI 版本不可覆盖，后续修改必须使用新版本号。

生命周期操作 `start/close/wait/wait_closed` 在主线程或应用控制线程调用；在 SDK 回调内调用会返回 invalid_state，避免关闭过程等待当前回调造成死锁。回调通过 atomic/Event/队列通知控制线程退出。Python Future 回调可能在 SDK 线程执行，也应保持非阻塞。
