Merge branch 'main' of ssh://892887cvmk08.aicp.io:24928/orisys-tech/orisys-app
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
|
||||
'*******************************************************ECAT总线初始化
|
||||
'''''''''
|
||||
|
||||
global CONST BUS_TYPE = 0 '总线类型。用于上位机区分当前模式
|
||||
global CONST MAX_AXISNUM = 32 '最大轴数
|
||||
global CONST Bus_Slot = 0 '槽位号0
|
||||
global CONST Bus_AxisStart = 0 '总线轴起始轴号
|
||||
|
||||
global Bus_InitStatus '总线初始化完成状态
|
||||
Bus_InitStatus = -1
|
||||
global Bus_TotalAxisnum '检查扫描的总轴数
|
||||
|
||||
delay(3000) '延时3S等待驱动器上电
|
||||
'**********************初始化ECAT总线
|
||||
Ecat_Init()
|
||||
|
||||
end
|
||||
|
||||
global sub Ecat_Init()
|
||||
for i=0 to MAX_AXISNUM - 1 '初始化还原轴类型
|
||||
AXIS_ENABLE(i) = 0
|
||||
atype(i)=0
|
||||
next
|
||||
|
||||
Bus_InitStatus = -1
|
||||
Bus_TotalAxisnum = 0
|
||||
SLOT_STOP(Bus_Slot)
|
||||
delay(200)
|
||||
slot_scan(Bus_Slot) '开始扫描
|
||||
if return then
|
||||
|
||||
?"总线扫描成功","连接设备数:"NODE_COUNT(Bus_Slot)
|
||||
?
|
||||
?"开始映射轴号"
|
||||
for i=0 to NODE_COUNT(Bus_Slot)-1 '遍历总线下所有从站节点
|
||||
|
||||
if NODE_AXIS_COUNT(Bus_Slot,i) <>0 then '判断当前节点是否有电机
|
||||
for j=0 to NODE_AXIS_COUNT(Bus_Slot,i)-1
|
||||
AXIS_ADDRESS(Bus_AxisStart+i)=Bus_TotalAxisnum+1 '映射轴号
|
||||
ATYPE(Bus_AxisStart+i)=65 '设置控制模式 65-位置 66-速度 67-转矩
|
||||
DRIVE_PROFILE(Bus_AxisStart+i)= 4 '设置PROFILE功能
|
||||
disable_group(Bus_AxisStart+i) '每轴单独分组
|
||||
|
||||
DRIVE_IO(Bus_AxisStart+i) = 128 + (Bus_AxisStart+i)*16 '映射驱动器上的IO状态
|
||||
REV_IN(Bus_AxisStart+i) = 128 + (Bus_AxisStart+i)*16
|
||||
FWD_IN(Bus_AxisStart+i) = 129 + (Bus_AxisStart+i)*16
|
||||
DATUM_IN(Bus_AxisStart+i) = 130 + (Bus_AxisStart+i)*16
|
||||
INVERT_IN(128 + (Bus_AxisStart+i)*16,ON)
|
||||
INVERT_IN(129 + (Bus_AxisStart+i)*16,ON)
|
||||
INVERT_IN(130 + (Bus_AxisStart+i)*16,ON)
|
||||
|
||||
Bus_TotalAxisnum=Bus_TotalAxisnum+1 '总轴数+1
|
||||
next
|
||||
endif
|
||||
|
||||
next
|
||||
|
||||
|
||||
' Axis 4 is a Panasonic MINAS A6 servo, not a Leadshine CL3B stepper:
|
||||
' its limit/datum inputs are active-high as the ZMC reads them, so they
|
||||
' must NOT be inverted like the steppers above. Override the loop for
|
||||
' that one axis only: Bus_TotalAxisnum-1 is the last axis the scan found,
|
||||
' which on this machine is the servo and never a stepper.
|
||||
INVERT_IN(128 + (Bus_AxisStart + Bus_TotalAxisnum - 1)*16,OFF)
|
||||
INVERT_IN(129 + (Bus_AxisStart + Bus_TotalAxisnum - 1)*16,OFF)
|
||||
INVERT_IN(130 + (Bus_AxisStart + Bus_TotalAxisnum - 1)*16,OFF)
|
||||
?"轴号映射完成","连接总轴数:"Bus_TotalAxisnum
|
||||
wa 2000
|
||||
|
||||
SLOT_START(Bus_Slot) '启动总线
|
||||
if return then
|
||||
?"总线开启成功"
|
||||
|
||||
?"开始清除驱动器错误(根据驱动器数据字典设置)"
|
||||
for i= Bus_AxisStart to Bus_AxisStart + Bus_TotalAxisnum - 1
|
||||
|
||||
DRIVE_CONTROLWORD(i)=128 '根据驱动器数据字典
|
||||
wa 100
|
||||
DRIVE_CONTROLWORD(i)=6
|
||||
wa 100
|
||||
DRIVE_CONTROLWORD(i)=15
|
||||
wa 100
|
||||
next
|
||||
|
||||
?"驱动器错误清除完成"
|
||||
wa 100
|
||||
|
||||
?"清除控制器错误"
|
||||
datum(0)
|
||||
DRIVE_CLEAR(0)
|
||||
?"控制器错误清除完成"
|
||||
wa 100
|
||||
|
||||
?"轴使能准备"
|
||||
for i= Bus_AxisStart to Bus_AxisStart + Bus_TotalAxisnum - 1
|
||||
base(i)
|
||||
AXIS_ENABLE=1
|
||||
next
|
||||
wdog=1 '使能总开关
|
||||
Bus_InitStatus = 1
|
||||
?"轴使能完成"
|
||||
else
|
||||
?"总线开启失败"
|
||||
Bus_InitStatus = 0
|
||||
endif
|
||||
|
||||
else
|
||||
?"总线扫描失败"
|
||||
Bus_InitStatus = 0
|
||||
endif
|
||||
end sub
|
||||
@@ -103,6 +103,8 @@
|
||||
"state_pending": "Not started",
|
||||
"state_running": "Initialising…",
|
||||
"state_ok": "Initialised",
|
||||
"state_unverified": "Init not confirmed",
|
||||
"unverified_hint": "The controller still holds the initialisation flag from its last run; this power-up has not re-scanned the bus. If the platform will not move, download the BAS file and re-initialise.",
|
||||
"state_failed": "Initialisation failed",
|
||||
"state_timeout": "Initialisation timed out",
|
||||
"nodes": "Nodes",
|
||||
|
||||
@@ -103,6 +103,8 @@
|
||||
"state_pending": "未完成",
|
||||
"state_running": "正在初始化…",
|
||||
"state_ok": "初始化成功",
|
||||
"state_unverified": "未确认初始化",
|
||||
"unverified_hint": "控制器保留了上次的总线初始化标志,本次开机尚未重新扫描;如无法运动,请先下载 BAS 文件并重新初始化。",
|
||||
"state_failed": "初始化失败",
|
||||
"state_timeout": "初始化超时",
|
||||
"nodes": "节点数量",
|
||||
|
||||
@@ -111,7 +111,16 @@ class AxisState:
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class BusStatus:
|
||||
"""Bus-level state, from the ``.bas`` program's globals."""
|
||||
"""Bus-level state, from the ``.bas`` program's globals.
|
||||
|
||||
``verified_this_session`` exists because ``BUS_TYPE``, ``Bus_InitStatus``
|
||||
and ``Bus_TotalAxisnum`` are ZBasic **globals**: they live in controller
|
||||
memory and survive the program finishing, a reconnect, and (with the ROM
|
||||
program reloading) a reboot. ``Bus_InitStatus == 1`` therefore means "a scan
|
||||
completed at some point", not "this bus is live now". A controller that was
|
||||
power-cycled but not re-initialised still reports 1, and reporting that as
|
||||
初始化成功 is what made a bus which had never been set up look ready.
|
||||
"""
|
||||
|
||||
bus_type: BusType = "unknown"
|
||||
init_status: int = INIT_RUNNING
|
||||
@@ -121,6 +130,14 @@ class BusStatus:
|
||||
bas_loaded: bool = False
|
||||
init_started_at: float | None = None
|
||||
message: str = ""
|
||||
#: False until this session has seen the state machine through a run it
|
||||
#: started (a download or a re-init). An adopted latch starts False.
|
||||
verified_this_session: bool = False
|
||||
|
||||
@property
|
||||
def adopted_unverified(self) -> bool:
|
||||
"""The latch says initialised, but nothing here has confirmed it."""
|
||||
return self.init_status == INIT_OK and not self.verified_this_session
|
||||
|
||||
@property
|
||||
def initialized(self) -> bool:
|
||||
|
||||
@@ -401,7 +401,9 @@ class MotionService:
|
||||
self._spec = spec
|
||||
logger.info("Connected: %s via %s", spec.describe(), self._transport.source)
|
||||
# A controller may already have a BAS program running from a previous
|
||||
# session; adopt it rather than claiming the bus is uninitialised.
|
||||
# session; adopt it rather than claiming the bus is uninitialised. The
|
||||
# adopted state is *not* marked verified: its globals are a latch, and
|
||||
# nothing in this session has yet seen a scan happen.
|
||||
self._adopt_existing_bas(bas_path=bas_path)
|
||||
return MotionResult.success(f"已连接 {spec.describe()}" if self._is_zh else f"Connected {spec.describe()}")
|
||||
|
||||
@@ -469,12 +471,14 @@ class MotionService:
|
||||
|
||||
self._bas_loaded = True
|
||||
self._init_started_at = time.monotonic()
|
||||
# A download is a run this session owns, so its outcome can be trusted.
|
||||
self._bus = BusStatus(
|
||||
bus_type=self._bus.bus_type,
|
||||
init_status=INIT_RUNNING,
|
||||
bas_path=filename,
|
||||
bas_loaded=True,
|
||||
init_started_at=self._init_started_at,
|
||||
verified_this_session=True,
|
||||
)
|
||||
# Read BUS_TYPE so the SDO/RTEX panels enable for the right family.
|
||||
try:
|
||||
@@ -541,6 +545,7 @@ class MotionService:
|
||||
bas_path=self._bus.bas_path,
|
||||
bas_loaded=True,
|
||||
init_started_at=self._init_started_at,
|
||||
verified_this_session=True,
|
||||
)
|
||||
# Re-read BUS_TYPE now: the program may be a different family than the
|
||||
# one the previous session left behind.
|
||||
@@ -662,7 +667,13 @@ class MotionService:
|
||||
)
|
||||
return
|
||||
|
||||
verified = bus.verified_this_session
|
||||
if init_status != INIT_RUNNING:
|
||||
# A run this session owned has just settled: that is the only
|
||||
# evidence available that the state machine actually moved, rather
|
||||
# than the globals having been left at their last values.
|
||||
if self._init_started_at is not None:
|
||||
verified = True
|
||||
self._init_started_at = None
|
||||
try:
|
||||
node_count = self._transport.bus_node_count(0)
|
||||
@@ -679,6 +690,7 @@ class MotionService:
|
||||
bas_loaded=True,
|
||||
init_started_at=self._init_started_at,
|
||||
message=message,
|
||||
verified_this_session=verified,
|
||||
)
|
||||
|
||||
def _read_axis(self, axis: int) -> AxisState:
|
||||
@@ -967,4 +979,7 @@ def _replace_bus_type(bus: BusStatus, bus_type: BusType) -> BusStatus:
|
||||
bas_loaded=bus.bas_loaded,
|
||||
init_started_at=bus.init_started_at,
|
||||
message=bus.message,
|
||||
# Carried, not reset: re-reading BUS_TYPE is incidental, and dropping
|
||||
# this would make a just-verified init look unverified again.
|
||||
verified_this_session=bus.verified_this_session,
|
||||
)
|
||||
|
||||
@@ -36,6 +36,7 @@ _STATE_KEYS = {
|
||||
_STATE_TONES = {
|
||||
"bus.state_running": "warn",
|
||||
"bus.state_ok": "ok",
|
||||
"bus.state_unverified": "warn",
|
||||
"bus.state_failed": "error",
|
||||
"bus.state_timeout": "error",
|
||||
}
|
||||
@@ -140,10 +141,19 @@ class BusPanel(QGroupBox):
|
||||
self.choose_button.setEnabled(True)
|
||||
|
||||
def set_status(self, bus: BusStatus, *, now: float | None = None) -> None:
|
||||
"""Render the bus state, including the vendor's timeout case."""
|
||||
"""Render the bus state, including the vendor's timeout case.
|
||||
|
||||
An *adopted* ``Bus_InitStatus == 1`` is shown as unverified rather than
|
||||
as success. The value is a ZBasic global that survives the program and a
|
||||
reboot, so it proves a scan happened at some point, not that this bus is
|
||||
live now — and calling that 初始化成功 is what made a machine which had
|
||||
never been set up look ready to move.
|
||||
"""
|
||||
key = _STATE_KEYS.get(bus.init_status, "bus.state_pending")
|
||||
if bus.init_status == INIT_FAILED and bus.message and "超时" in bus.message:
|
||||
key = "bus.state_timeout"
|
||||
elif bus.adopted_unverified:
|
||||
key = "bus.state_unverified"
|
||||
self._state_text = t(key)
|
||||
self._state_tone = _STATE_TONES.get(key, "muted")
|
||||
self.state_value.setText(self._state_text)
|
||||
@@ -156,7 +166,7 @@ class BusPanel(QGroupBox):
|
||||
self.type_value.setText(t(type_keys.get(bus.bus_type, "bus.type_unknown")))
|
||||
self.nodes_value.setText(str(bus.node_count))
|
||||
self.axes_value.setText(str(bus.total_axes))
|
||||
self.message_label.setText(bus.message)
|
||||
self.message_label.setText(bus.message or (t("bus.unverified_hint") if bus.adopted_unverified else ""))
|
||||
|
||||
# ------------------------------------------------------------- translations
|
||||
|
||||
|
||||
@@ -224,6 +224,12 @@ class StatusStrip(QFrame):
|
||||
if not connected:
|
||||
self.chip_init.setText(t("bus.state_pending"))
|
||||
self.chip_init.set_tone("muted")
|
||||
elif snapshot.bus.adopted_unverified:
|
||||
# The controller's initialisation global is a latch that survives the
|
||||
# program and a reboot; without a run this session, "initialised" is
|
||||
# not something this process has seen. Say so rather than claim it.
|
||||
self.chip_init.setText(t("bus.state_unverified"))
|
||||
self.chip_init.set_tone("warn")
|
||||
elif snapshot.bus.initialized:
|
||||
self.chip_init.setText(t("bus.state_ok"))
|
||||
self.chip_init.set_tone("ok")
|
||||
|
||||
@@ -878,7 +878,9 @@ class AutoCalMainWindow(QMainWindow):
|
||||
t("status.poll_paused") if self.status_strip.paused else t("status.poll_ok"),
|
||||
t("status.poll_every", ms=self.poller.interval_ms),
|
||||
]
|
||||
if snapshot.bus.init_status == 1:
|
||||
if snapshot.bus.adopted_unverified:
|
||||
parts.append(t("bus.state_unverified"))
|
||||
elif snapshot.bus.init_status == 1:
|
||||
parts.append(t("bus.state_ok"))
|
||||
if self._last_error:
|
||||
parts.append(f"{t('status.last_error')}: {self._last_error}")
|
||||
|
||||
Reference in New Issue
Block a user