Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions src/emc/task/taskclass.cc
Original file line number Diff line number Diff line change
Expand Up @@ -587,14 +587,15 @@ int Task::emcToolSetOffset(int idx, int toolno, const EmcPose& offset, double di
if (0 != tooldata_save(tooltable_filename)) {
emcioStatus.status = RCS_STATUS::ERROR;
}
//TODO
// if (io_db_mode == DB_ACTIVE) {
// int pno = idx; // for random_toolchanger
// if (!random_toolchanger) { pno = tdata.pocketno; }
// if (tooldata_db_notify(TOOL_OFFSET,toolno,pno,tdata)) {
// UNEXPECTED_MSG;
// }
// }
if (db_mode == tooldb_t::DB_ACTIVE) {
// tooldata_save() does not write tool table entries in db mode,
// so the db_program must be notified to persist the new offsets
int pno = idx; // for random_toolchanger
if (!random_toolchanger) { pno = tdata.pocketno; }
if (tooldata_db_notify(TOOL_OFFSET,toolno,pno,tdata)) {
UNEXPECTED_MSG;
}
}

return 0;
}
Expand Down
2 changes: 2 additions & 0 deletions tests/tooldb/db-g10-offset/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
db_tools.txt
db_cmds.log
6 changes: 6 additions & 0 deletions tests/tooldb/db-g10-offset/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Verify that tool offsets set with G10 L1/L10/L11 are persisted when an
[EMCIO]DB_PROGRAM is configured.

In db mode with a nonrandom toolchanger no tool table file is written, so the
db program must be notified of the new offsets ('p' command). Without that
notification the offsets only live in memory and are lost on the next start.
98 changes: 98 additions & 0 deletions tests/tooldb/db-g10-offset/base.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
[EMC]
# The version string for this INI file.
VERSION = 1.1
DEBUG = 0x0

[RS274NGC]
PARAMETER_FILE = sim.var

[EMCMOT]
EMCMOT = motmod
COMM_TIMEOUT = 4.0
BASE_PERIOD = 0
SERVO_PERIOD = 1000000

[TASK]
TASK = milltask
CYCLE_TIME = 0.001

[HAL]
# the library file provides the estop and toolchange loopbacks needed here
HALFILE = LIB:core_sim.hal

[TRAJ]
NO_FORCE_HOMING=1
COORDINATES = XYZ
HOME = 0 0 0
LINEAR_UNITS = inch
ANGULAR_UNITS = degree
DEFAULT_LINEAR_VELOCITY = 1.2
MAX_LINEAR_ACCELERATION = 123.45
MAX_LINEAR_VELOCITY = 45.67

[EMCIO]
# no TOOL_TABLE: tool data is provided by the db program
TOOL_CHANGE_QUILL_UP = 1
RANDOM_TOOLCHANGER = 0
DB_PROGRAM = ./db_test.py

[KINS]
KINEMATICS = trivkins coordinates=XYZ
JOINTS = 3

[AXIS_X]
MIN_LIMIT = -40.0
MAX_LIMIT = 40.0
MAX_VELOCITY = 4
MAX_ACCELERATION = 1000.0

[JOINT_0]
TYPE = LINEAR
HOME = 0.000
MAX_VELOCITY = 4
MAX_ACCELERATION = 1000.0
BACKLASH = 0.000
INPUT_SCALE = 4000
OUTPUT_SCALE = 1.000
MIN_LIMIT = -40.0
MAX_LIMIT = 40.0
FERROR = 0.050
MIN_FERROR = 0.010

[AXIS_Y]
MIN_LIMIT = -40.0
MAX_LIMIT = 40.0
MAX_VELOCITY = 4
MAX_ACCELERATION = 1000.0

[JOINT_1]
TYPE = LINEAR
HOME = 0.000
MAX_VELOCITY = 4
MAX_ACCELERATION = 1000.0
BACKLASH = 0.000
INPUT_SCALE = 4000
OUTPUT_SCALE = 1.000
MIN_LIMIT = -40.0
MAX_LIMIT = 40.0
FERROR = 0.050
MIN_FERROR = 0.010

[AXIS_Z]
MIN_LIMIT = -40.0
MAX_LIMIT = 40.0
MAX_VELOCITY = 4
MAX_ACCELERATION = 1000.0

[JOINT_2]
TYPE = LINEAR
HOME = 0.0
MAX_VELOCITY = 4
MAX_ACCELERATION = 1000.0
BACKLASH = 0.000
INPUT_SCALE = 4000
OUTPUT_SCALE = 1.000
MIN_LIMIT = -40.0
MAX_LIMIT = 40.0
FERROR = 0.050
MIN_FERROR = 0.010
2 changes: 2 additions & 0 deletions tests/tooldb/db-g10-offset/checkresult
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/sh
exit 0 # test failure is indicated by test.sh exit value
80 changes: 80 additions & 0 deletions tests/tooldb/db-g10-offset/db_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/usr/bin/env python3
# Minimal [EMCIO]DB_PROGRAM used by this test.
# Tool data is kept in a flat file (db_tools.txt) so that it survives a
# restart of LinuxCNC, and every received command is appended to db_cmds.log.
# Note: stdout is the pipe to LinuxCNC, so logging goes to a file.

import os

from tooldb import tooldb_callbacks
from tooldb import tooldb_tools
from tooldb import tooldb_loop

savefile = "db_tools.txt"
logfile = "db_cmds.log"

initial_tools = {
1: "T1 P1 D0.1 Z0.5",
2: "T2 P2 D0.2 Z1.5",
3: "T3 P3 D0.3 Z2.5",
}

tools = {}

def log(msg):
with open(logfile,"a") as f:
f.write("%s\n"%msg)

def save():
with open(savefile,"w") as f:
for tno in sorted(tools):
f.write("%s\n"%tools[tno])

def load():
if not os.path.exists(savefile):
tools.update(initial_tools)
save()
return
with open(savefile) as f:
for line in f:
line = line.strip()
if not line: continue
tools[toolno_of(line)] = line

def toolno_of(toolline):
for item in toolline.upper().split():
if item.startswith("T"): return int(item[1:])
raise ValueError("no toolno in <%s>"%toolline)

def merge(toolline,params):
# apply the letter parameters of params to toolline
D = dict((item[0],item[1:]) for item in toolline.upper().split())
for item in params.upper().split():
if item.startswith(";"): break
D[item[0]] = item[1:]
letters = ["T","P"] + sorted(set(D) - set(["T","P"]))
return " ".join("%s%s"%(letter,D[letter]) for letter in letters)

# 'g' interface command
def get_tool(tno):
log("g %s"%tools[tno])
return tools[tno]

# 'p' interface command
def put_tool(tno,params):
log("p %s"%params)
tools[tno] = merge(tools.get(tno,"T%d P%d"%(tno,tno)),params)
save()

# 'l' interface command
def load_spindle(tno,params):
log("l %s"%params)

# 'u' interface command
def unload_spindle(tno,params):
log("u %s"%params)

load()
tooldb_callbacks(get_tool,put_tool,load_spindle,unload_spindle)
tooldb_tools(sorted(tools))
tooldb_loop()
82 changes: 82 additions & 0 deletions tests/tooldb/db-g10-offset/dbtest_lib.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# helpers shared by the ui scripts of this test

import sys
import time

import linuxcnc

savefile = "db_tools.txt"


def wait_for_linuxcnc_startup(status, timeout=10.0):
"""Poll the Status buffer waiting for it to look initialized, rather
than just allocated (all-zero)."""
start_time = time.time()
while time.time() - start_time < timeout:
status.poll()
if (status.angular_units == 0.0) \
or (status.axis_mask == 0) \
or (status.cycle_time == 0.0) \
or (status.exec_state != linuxcnc.EXEC_DONE) \
or (status.interp_state != linuxcnc.INTERP_IDLE) \
or (status.inpos is False) \
or (status.linear_units == 0.0) \
or (status.max_acceleration == 0.0) \
or (status.max_velocity == 0.0) \
or (status.program_units == 0.0) \
or (status.rapidrate == 0.0) \
or (status.state != linuxcnc.RCS_DONE) \
or (status.task_state != linuxcnc.STATE_ESTOP):
time.sleep(0.1)
else:
return
raise RuntimeError("timeout waiting for linuxcnc startup")


def start_machine():
c = linuxcnc.command()
s = linuxcnc.stat()
wait_for_linuxcnc_startup(s)
c.state(linuxcnc.STATE_ESTOP_RESET)
c.state(linuxcnc.STATE_ON)
c.home(-1)
c.wait_complete()
c.mode(linuxcnc.MODE_MDI)
c.wait_complete()
return (c, s)


def fail(msg):
print("FAIL: %s" % msg)
sys.exit(1)


def check(what, got, expected):
if abs(got - expected) > 1e-6:
fail("%s: expected %.6f, got %.6f" % (what, expected, got))
print("ok: %s = %.6f" % (what, got))


def tool_table_zoffset(s, idx):
s.poll()
return s.tool_table[idx].zoffset


def db_zoffset(toolno, timeout=0):
"""Z offset recorded by the db program for toolno, or None."""
deadline = time.time() + timeout
while True:
try:
with open(savefile) as f:
for line in f:
items = line.upper().split()
if "T%d" % toolno not in items:
continue
for item in items:
if item.startswith("Z"):
return float(item[1:])
except IOError:
pass
if time.time() >= deadline:
return None
time.sleep(0.1)
50 changes: 50 additions & 0 deletions tests/tooldb/db-g10-offset/test-ui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env python3

# With an [EMCIO]DB_PROGRAM configured and a nonrandom toolchanger, no tool
# table file is written, so G10 L1/L10/L11 offsets are persisted only if the
# db program is notified ('p' command).

import sys

from dbtest_lib import start_machine, check, fail, tool_table_zoffset, db_zoffset

(c, s) = start_machine()

# initial values come from the db program
check("tool 3 zoffset at startup", tool_table_zoffset(s, 3), 2.5)
check("db zoffset for tool 3 at startup", db_zoffset(3), 2.5)

# 1) G10 L1 on a tool that is not in the spindle
c.mdi("G10 L1 P3 Z-12.345")
c.wait_complete()

check("tool 3 zoffset after G10 L1", tool_table_zoffset(s, 3), -12.345)

got = db_zoffset(3, timeout=5)
if got is None:
fail("db program was not notified of the G10 L1 offset for tool 3")
check("db zoffset for tool 3 after G10 L1", got, -12.345)

# 2) G10 L1 on the tool that is in the spindle
c.mdi("T2 M6")
c.wait_complete()
s.poll()
if s.tool_in_spindle != 2:
fail("expected tool 2 in spindle, got %d" % s.tool_in_spindle)

c.mdi("G10 L1 P2 Z-6.789")
c.wait_complete()

check("tool 2 zoffset after G10 L1", tool_table_zoffset(s, 2), -6.789)
check("spindle zoffset after G10 L1", tool_table_zoffset(s, 0), -6.789)

got = db_zoffset(2, timeout=5)
if got is None:
fail("db program was not notified of the G10 L1 offset for tool 2")
check("db zoffset for tool 2 after G10 L1", got, -6.789)

# unload the tool so the db program sees a clean shutdown
c.mdi("T0 M6")
c.wait_complete()

sys.exit(0)
5 changes: 5 additions & 0 deletions tests/tooldb/db-g10-offset/test.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# apply G10 L1 tool offsets with an [EMCIO]DB_PROGRAM configured
[DISPLAY]
DISPLAY = ./test-ui.py

#INCLUDE base.inc
11 changes: 11 additions & 0 deletions tests/tooldb/db-g10-offset/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash
set -x
set -e

rm -f sim.var* db_tools.txt db_cmds.log

# run 1: apply G10 L1 offsets (db program creates db_tools.txt)
linuxcnc -r test.ini

# run 2: the offsets must be restored from the db program
linuxcnc -r verify.ini
16 changes: 16 additions & 0 deletions tests/tooldb/db-g10-offset/verify-ui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env python3

# Second run: the tool data now comes from the db program, and must carry the
# offsets written by test-ui.py in the previous run.

import sys

from dbtest_lib import start_machine, check, tool_table_zoffset

(c, s) = start_machine()

check("tool 3 zoffset after restart", tool_table_zoffset(s, 3), -12.345)
check("tool 2 zoffset after restart", tool_table_zoffset(s, 2), -6.789)
check("tool 1 zoffset after restart", tool_table_zoffset(s, 1), 0.5)

sys.exit(0)
5 changes: 5 additions & 0 deletions tests/tooldb/db-g10-offset/verify.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# restart: the offsets applied by test.ini must come back from the db program
[DISPLAY]
DISPLAY = ./verify-ui.py

#INCLUDE base.inc