Merge 5a4a5d30d4
into 58c3f6240d
This commit is contained in:
commit
4ba34f08a2
|
@ -0,0 +1,126 @@
|
||||||
|
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
|
||||||
|
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
|
||||||
|
|
||||||
|
name: Python package
|
||||||
|
run-name: ${{ github.actor }} package
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
c_cyclonedds_version:
|
||||||
|
description: 'clang cyclonedds version'
|
||||||
|
required: false
|
||||||
|
default: 'releases/0.10.x'
|
||||||
|
cyclonedds_python_version:
|
||||||
|
description: cyclonedds_python的版本
|
||||||
|
required: false
|
||||||
|
default: 'releases/0.10.x'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
# 输入参数定义部分
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
python-version: ["3.11"]
|
||||||
|
os: [macos-latest, windows-latest, ubuntu-latest]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
# 初始化
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# 配置python
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v3
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
|
# 初始化制品收集目录
|
||||||
|
- name: init dist
|
||||||
|
run: |
|
||||||
|
mkdir dist
|
||||||
|
|
||||||
|
# 准备构建python wheel包
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install build
|
||||||
|
|
||||||
|
# 由于cyclonedds-python暂时没有提供python3.11 顺带也编译了 issue:https://github.com/eclipse-cyclonedds/cyclonedds-python/issues/221
|
||||||
|
- name: build cyclonedds-python in windows
|
||||||
|
if: startsWith(runner.os, 'Windows')
|
||||||
|
run: |
|
||||||
|
git clone https://github.com/eclipse-cyclonedds/cyclonedds-python -b ${{ github.event.inputs.cyclonedds_python_version }} --depth=1
|
||||||
|
cd cyclonedds-python
|
||||||
|
(Get-Content pyproject.toml) -replace 'https://github.com/eclipse-cyclonedds/cyclonedds.git','https://github.com/eclipse-cyclonedds/cyclonedds -b ${{ github.event.inputs.c_cyclonedds_version }} --depth=1' | Set-Content pyproject.toml
|
||||||
|
(Get-Content pyproject.toml) -replace '(?<=^skip = ")','cp3{6..10}-* ' | Set-Content pyproject.toml
|
||||||
|
(Get-Content pyproject.toml) -replace 'delvewheel==0.0.18','delvewheel==1.6.0' | Set-Content pyproject.toml
|
||||||
|
(Get-Content setup.py) -replace '(?<="Programming Language :: Python :: 3.10",.*?)', "`n `"Programming Language :: Python :: 3.11`",`n `"Programming Language :: Python :: 3.12`",`n `"Programming Language :: Python :: 3.13`"," | Set-Content setup.py
|
||||||
|
pip install --user cibuildwheel==2.18.*
|
||||||
|
python -m cibuildwheel --output-dir wheelhouse .
|
||||||
|
tar zcvf cyclonedds.tar.gz -C cyclonedds-build .
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
# 兼容sed see:https://gist.github.com/andre3k1/e3a1a7133fded5de5a9ee99c87c6fa0d
|
||||||
|
- name: install GNU sed in mac
|
||||||
|
if: startsWith(runner.os, 'macOS')
|
||||||
|
run: |
|
||||||
|
brew install gnu-sed
|
||||||
|
|
||||||
|
- name: build cyclonedds-python in unix
|
||||||
|
if: startsWith(runner.os, 'Linux') || startsWith(runner.os, 'macOS')
|
||||||
|
run: |
|
||||||
|
if [ "${{ startsWith(runner.os, 'macOS') }}" ]; then
|
||||||
|
PATH="/opt/homebrew/opt/gnu-sed/libexec/gnubin:$PATH"
|
||||||
|
fi
|
||||||
|
git clone https://github.com/eclipse-cyclonedds/cyclonedds-python -b ${{ github.event.inputs.c_cyclonedds_version }} --depth=1
|
||||||
|
cd cyclonedds-python
|
||||||
|
|
||||||
|
sed -i "s~https://github.com/eclipse-cyclonedds/cyclonedds.git~https://github.com/eclipse-cyclonedds/cyclonedds -b ${{ github.event.inputs.c_cyclonedds_version }} --depth=1~g" pyproject.toml
|
||||||
|
echo "debugprintpoint1"
|
||||||
|
sed -i 's/\(^skip = "\)/\1cp3{6..10}-* /g' pyproject.toml
|
||||||
|
echo "printpoint2"
|
||||||
|
sed -i 's/delvewheel==0\.0\.18/delvewheel==1\.6\.0/g' pyproject.toml
|
||||||
|
echo "printpoint3"
|
||||||
|
sed -i 's/(?<="Programming Language :: Python :: 3.10",.*?)/\n "Programming Language :: Python :: 3.11",\n "Programming Language :: Python :: 3.12",\n "Programming Language :: Python :: 3.13",/g' setup.py
|
||||||
|
pip install --user cibuildwheel==2.18.*
|
||||||
|
python -m cibuildwheel --output-dir wheelhouse .
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
# 构建宇树python sdk本身
|
||||||
|
- name: build package unix
|
||||||
|
if: startsWith(runner.os, 'Linux') || startsWith(runner.os, 'macOS')
|
||||||
|
run: |
|
||||||
|
export CYCLONEDDS_HOME=${{ github.workspace }}/cyclonedds/install
|
||||||
|
python -m build --wheel
|
||||||
|
shell: bash
|
||||||
|
- name: build package windows
|
||||||
|
if: startsWith(runner.os, 'Windows')
|
||||||
|
run: |
|
||||||
|
set CYCLONEDDS_HOME=${{ github.workspace }}/cyclonedds/install
|
||||||
|
python -m build --wheel
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
|
# 收集构建产物
|
||||||
|
- name: collect binary
|
||||||
|
if: startsWith(runner.os, 'Windows')
|
||||||
|
run: |
|
||||||
|
mv ${{ github.workspace }}/cyclonedds-python/wheelhouse/* dist/
|
||||||
|
mv ${{ github.workspace }}/dist/* dist/
|
||||||
|
- name: collect binary
|
||||||
|
if: startsWith(runner.os, 'Linux') || startsWith(runner.os, 'macOS')
|
||||||
|
run: |
|
||||||
|
# 对复杂的不同平台目录机制信息收集
|
||||||
|
find . -print | sed -e 's;[^/]*/;|____;g;s;____|; |;g' > dist/${{runner.os}}_${{runner.arch}}.txt
|
||||||
|
mv -f ${{ github.workspace }}/cyclonedds-python/wheelhouse/* dist/
|
||||||
|
# mv -f ${{ github.workspace }}/dist/* dist/
|
||||||
|
|
||||||
|
# 上传到工作流制品库
|
||||||
|
- name: upload package and dependency
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: unitree_sdk2_python${{ matrix.python-version }}_${{runner.os}}_${{runner.arch}}
|
||||||
|
path: |
|
||||||
|
dist/*
|
||||||
|
|
|
@ -34,3 +34,6 @@ __pycache__
|
||||||
|
|
||||||
# JetBrains IDE
|
# JetBrains IDE
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
build
|
||||||
|
dist
|
|
@ -0,0 +1,21 @@
|
||||||
|
import os,sys
|
||||||
|
|
||||||
|
|
||||||
|
def check_init_files(directory):
|
||||||
|
"""递归确认包存在__init__.py防止缺胳膊少腿"""
|
||||||
|
all_dirs_have_init = True
|
||||||
|
for root, dirs, files in os.walk(directory):
|
||||||
|
if "__pycache__" in dirs:
|
||||||
|
dirs.remove("__pycache__")
|
||||||
|
if "test" in dirs:
|
||||||
|
dirs.remove("test")
|
||||||
|
if "__init__.py" not in files:
|
||||||
|
print(f"Directory '{root}' is missing '__init__.py'")
|
||||||
|
all_dirs_have_init = False
|
||||||
|
return all_dirs_have_init
|
||||||
|
|
||||||
|
directory_to_check = os.path.join(os.path.dirname(os.path.realpath(__file__)),"..","unitree_sdk2py")
|
||||||
|
if check_init_files(directory_to_check):
|
||||||
|
print("所有文件夹均含 '__init__.py'.")
|
||||||
|
else:
|
||||||
|
print("校验未通过", file=sys.stderr)
|
|
@ -1,4 +1,13 @@
|
||||||
ChannelConfigHasInterface = '''<?xml version="1.0" encoding="UTF-8" ?>
|
import platform
|
||||||
|
|
||||||
|
# build platform compatible
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
import os
|
||||||
|
tmp = os.environ['TEMP']
|
||||||
|
else:
|
||||||
|
tmp = "/tmp"
|
||||||
|
|
||||||
|
ChannelConfigHasInterface = f'''<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<CycloneDDS>
|
<CycloneDDS>
|
||||||
<Domain Id="any">
|
<Domain Id="any">
|
||||||
<General>
|
<General>
|
||||||
|
@ -8,7 +17,7 @@ ChannelConfigHasInterface = '''<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
</General>
|
</General>
|
||||||
<Tracing>
|
<Tracing>
|
||||||
<Verbosity>config</Verbosity>
|
<Verbosity>config</Verbosity>
|
||||||
<OutputFile>/tmp/cdds.LOG</OutputFile>
|
<OutputFile>{tmp}/cdds.LOG</OutputFile>
|
||||||
</Tracing>
|
</Tracing>
|
||||||
</Domain>
|
</Domain>
|
||||||
</CycloneDDS>'''
|
</CycloneDDS>'''
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
from unitree_sdk2py.utils.hz_sample import HZSample
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
hz=HZSample(2)
|
||||||
|
hz.Start()
|
||||||
|
while True:
|
||||||
|
hz.Sample()
|
||||||
|
|
|
@ -52,10 +52,7 @@ class RecurrentThread(Thread):
|
||||||
super().Wait(timeout)
|
super().Wait(timeout)
|
||||||
|
|
||||||
def __LoopFunc(self):
|
def __LoopFunc(self):
|
||||||
# clock type CLOCK_MONOTONIC = 1
|
timer = Timer(self.__inter, self.__inter)
|
||||||
tfd = timerfd_create(1, 0)
|
|
||||||
spec = itimerspec.from_seconds(self.__inter, self.__inter)
|
|
||||||
timerfd_settime(tfd, 0, ctypes.byref(spec), None)
|
|
||||||
|
|
||||||
while not self.__quit:
|
while not self.__quit:
|
||||||
try:
|
try:
|
||||||
|
@ -65,13 +62,13 @@ class RecurrentThread(Thread):
|
||||||
print(f"[RecurrentThread] target func raise exception: name={info[0].__name__}, args={str(info[1].args)}")
|
print(f"[RecurrentThread] target func raise exception: name={info[0].__name__}, args={str(info[1].args)}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
buf = os.read(tfd, 8)
|
timer.blockWait()
|
||||||
# print(struct.unpack("Q", buf)[0])
|
# print(struct.unpack("Q", buf)[0])
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno != errno.EAGAIN:
|
if e.errno != errno.EAGAIN:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
os.close(tfd)
|
timer.close()
|
||||||
|
|
||||||
def __LoopFunc_0(self):
|
def __LoopFunc_0(self):
|
||||||
while not self.__quit:
|
while not self.__quit:
|
||||||
|
|
|
@ -1,8 +1,62 @@
|
||||||
import math
|
import math
|
||||||
import ctypes
|
import ctypes
|
||||||
from .clib_lookup import CLIBLookup
|
import platform
|
||||||
|
|
||||||
class timespec(ctypes.Structure):
|
"""声明计时通用接口,主要为兼容windows平台和linux的文件描述符,在python3.13 os会支持但现在还没release.."""
|
||||||
|
class Timer:
|
||||||
|
|
||||||
|
"""
|
||||||
|
参数:
|
||||||
|
time: 首次运行等待时间,秒.First Wait Time, second.
|
||||||
|
period: 周期等待时间, 秒.Period Wait Time, second.
|
||||||
|
"""
|
||||||
|
def __init__(self, time :float, period :float):
|
||||||
|
pass
|
||||||
|
|
||||||
|
"""
|
||||||
|
阻塞等待,会在下方根据平台重写
|
||||||
|
"""
|
||||||
|
def blockWait(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
"""
|
||||||
|
关闭句柄
|
||||||
|
"""
|
||||||
|
def close(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# build platform compatible
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
from ctypes import wintypes
|
||||||
|
kernel32 = ctypes.windll.kernel32
|
||||||
|
INFINITE=wintypes.DWORD(-1)
|
||||||
|
PTIMERAPCROUTINE = ctypes.WINFUNCTYPE(None, wintypes.LPVOID, wintypes.DWORD, wintypes.DWORD)
|
||||||
|
@PTIMERAPCROUTINE
|
||||||
|
def timer_callback(arg, timer_low, timer_high):
|
||||||
|
pass
|
||||||
|
# 重写Timer方法
|
||||||
|
def Timer_init(self, time :float, period :float):
|
||||||
|
self.handle = kernel32.CreateWaitableTimerW(None, True, None)
|
||||||
|
due_time = wintypes.LARGE_INTEGER(-int(time * 10000000)) # 秒转100纳秒
|
||||||
|
period = int(period*1000) # 秒转毫秒
|
||||||
|
# https://learn.microsoft.com/zh-cn/windows/win32/api/synchapi/nf-synchapi-setwaitabletimer
|
||||||
|
if not kernel32.SetWaitableTimer(self.handle, ctypes.byref(due_time), period, timer_callback, 0, True):
|
||||||
|
raise OSError("Failed to set waitable timer.")
|
||||||
|
def Timer_blockWait(self):
|
||||||
|
kernel32.WaitForSingleObject(self.handle, INFINITE)
|
||||||
|
def Timer_close(self):
|
||||||
|
kernel32.CancelWaitableTimer(self.handle)
|
||||||
|
kernel32.CloseHandle(self.handle)
|
||||||
|
Timer.__init__ = Timer_init
|
||||||
|
Timer.blockWait = Timer_blockWait
|
||||||
|
Timer.close = Timer_close
|
||||||
|
|
||||||
|
elif platform.system() == "Linux":
|
||||||
|
|
||||||
|
from .clib_lookup import CLIBLookup
|
||||||
|
|
||||||
|
class timespec(ctypes.Structure):
|
||||||
_fields_ = [("sec", ctypes.c_long), ("nsec", ctypes.c_long)]
|
_fields_ = [("sec", ctypes.c_long), ("nsec", ctypes.c_long)]
|
||||||
__slots__ = [name for name,type in _fields_]
|
__slots__ = [name for name,type in _fields_]
|
||||||
|
|
||||||
|
@ -23,7 +77,7 @@ class timespec(ctypes.Structure):
|
||||||
self.nsec = int(x * 1000000000)
|
self.nsec = int(x * 1000000000)
|
||||||
|
|
||||||
|
|
||||||
class itimerspec(ctypes.Structure):
|
class itimerspec(ctypes.Structure):
|
||||||
_fields_ = [("interval", timespec),("value", timespec)]
|
_fields_ = [("interval", timespec),("value", timespec)]
|
||||||
__slots__ = [name for name,type in _fields_]
|
__slots__ = [name for name,type in _fields_]
|
||||||
|
|
||||||
|
@ -35,11 +89,28 @@ class itimerspec(ctypes.Structure):
|
||||||
return spec
|
return spec
|
||||||
|
|
||||||
|
|
||||||
# function timerfd_create
|
# function timerfd_create
|
||||||
timerfd_create = CLIBLookup("timerfd_create", ctypes.c_int, (ctypes.c_long, ctypes.c_int))
|
timerfd_create = CLIBLookup("timerfd_create", ctypes.c_int, (ctypes.c_long, ctypes.c_int))
|
||||||
|
|
||||||
# function timerfd_settime
|
# function timerfd_settime
|
||||||
timerfd_settime = CLIBLookup("timerfd_settime", ctypes.c_int, (ctypes.c_int, ctypes.c_int, ctypes.POINTER(itimerspec), ctypes.POINTER(itimerspec)))
|
timerfd_settime_linux = CLIBLookup("timerfd_settime", ctypes.c_int, (ctypes.c_int, ctypes.c_int, ctypes.POINTER(itimerspec), ctypes.POINTER(itimerspec)))
|
||||||
|
|
||||||
# function timerfd_gettime
|
def timerfd_settime(handle,interval,value):
|
||||||
timerfd_gettime = CLIBLookup("timerfd_gettime", ctypes.c_int, (ctypes.c_int, ctypes.POINTER(itimerspec)))
|
spec = itimerspec.from_seconds(interval, value)
|
||||||
|
timerfd_settime_linux(handle, 0, spec, None)
|
||||||
|
|
||||||
|
# function timerfd_gettime
|
||||||
|
timerfd_gettime = CLIBLookup("timerfd_gettime", ctypes.c_int, (ctypes.c_int, ctypes.POINTER(itimerspec)))
|
||||||
|
# 重写Timer方法
|
||||||
|
import os
|
||||||
|
def Timer_init(self, time :float, period :float):
|
||||||
|
self.handle = timerfd_create(1, 0)
|
||||||
|
spec = itimerspec.from_seconds(period, time)
|
||||||
|
timerfd_settime_linux(self.handle, 0, spec, None)
|
||||||
|
def Timer_blockWait(self):
|
||||||
|
os.read(self.handle,8)
|
||||||
|
def Timer_close(self):
|
||||||
|
os.close(self.handle)
|
||||||
|
Timer.__init__ = Timer_init
|
||||||
|
Timer.blockWait = Timer_blockWait
|
||||||
|
Timer.close = Timer_close
|
Loading…
Reference in New Issue