Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scriptable Motor Controller #79

Merged
merged 5 commits into from
May 2, 2018
Merged

Scriptable Motor Controller #79

merged 5 commits into from
May 2, 2018

Conversation

keenanlang
Copy link
Member

Original pull request accidentally got merged when I was trying to fix build issues.

This pull request adds a Model 3 driver that runs functions from a lua scripting language file.

Having a single motor driver than can run code from an easily modifiable text file grants a lot of benefits.

  • All the background C code (include headers, constructors, function registration) no longer has to be copied between new drivers, leading to a drastically reduced file footprint (a 500 line driver down to ~50).
  • Due to the ability to reload support on the fly, a developer doesn't have to rebuild and reboot an IOC to see changes to their driver, leading to a drastically shortened development cycle.
  • And since nothing needs to be compiled, support can easily be customized or modified for an IOC as the need arises. If a vendor releases a motor that's just slightly different than others of its type or a motor isn't working to spec, a quick copy/paste of the lua script to the IOC and a few tweaks can give a working driver.

How it works

A ScriptMotorController is created by calling the iocsh function ScriptControllerConfig. It takes as its parameters; the name of the asyn port to create, the number of axes this controller will control, the name of the lua file that contains the commands to run the support, and an optional set of parameters defined in the xxx=yyy format that will become global parameters for all the axes that are defined by this controller. The driver searches for the file by using the LUA_SCRIPT_PATH environment variable, a semicolon separated list of folders that may contain the script. Once found, the controller will compile the file into a bytecode state and each axis of the controller gets a copy of said state. The polling rate of this controller is set by the variables IdlePollPeriod and MovingPollPeriod, either setting them in the parameter list, or by setting them in the lua script itself.

Further parameters can then be supplied to each individual axis by using the ScriptAxisConfig function. It takes in the controller's port name, the axis you are supplying parameters to, and another set of parameters to become global variables for the lua state of that motor axis.

Whenever one of the asynMotorAxis commands is called, the driver attempts to find the same function defined in the script and call it with the same parameters that it was given. So, for example, when the move command is issued to the motor driver, it looks for a function named 'move' in the lua script and then calls it with the position, relative movement, minimum velocity, maximum velocity, and acceleration parameters.

Inside each of these functions, control of the motor record can be accomplished by setting asyn parameters and processing callbacks. The relevant functions are asyn.getXXXXParam, asyn.setXXXXParam, and asyn.callParamCallbacks. They each take as their first two parameters the port name that refers to the ScriptMotorController driver and the axis number of the motor running the code. Both are conveniently provided as global variables DRIVER and AXIS. The get and set param functions next take the string name of a parameter that the motor controller provides. And the set param function will then take a value to set that parameter to, while the get param function will return the value of the parameter. A small snippet of code using all of these would be:

-- Get motor resolution
local MRES = asyn.getDoubleParam( DRIVER, AXIS, "MOTOR_REC_RESOLUTION")

-- Convert actual position into number of steps
asyn.setDoubleParam( DRIVER, AXIS, "MOTOR_POSITION", actual_position / MRES )

-- Do callbacks
asyn.callParamCallbacks( DRIVER, AXIS )

Control of the motor itself is usually done by sending and receiving data across an asyn port. The methods included to allow that are:

asyn.write(data, PORT)
asyn.writeread(data, PORT)
asyn.read(PORT)

where data is a string to sent to an asyn port, and PORT is the name of the port itself. Usually PORT will be supplied to the script through the parameter system noted above. These functions use a set of parameters to control their functionality. These parameters are the same as the ones from the stream module:

OutTerminator
InTerminator
WriteTimeout
ReadTimeout

which can all either be set by just assigning a value to said variable or using a set function:

SetOutTerminator
SetInTerminator
SetWriteTimeout
SetReadTimeout

Examples of using all of this is provided with the vmc.lua script.

In addition, there are the epics.get and epics.put functions that replicate caget and caput to allow the script to read and write to any PV. The softMotor.lua script provides an example of using these functions to create a motor wrapper around a drive value and a readback value.

Note

This pull request works using the epics-modules/lua module, which isn't a standard part of synApps yet. RELEASE has been changed to note that it is necessary and ScriptMotorController does not build by default.

@kmpeters
Copy link
Member

kmpeters commented Apr 27, 2018

@keenanlang,

I not able to build the ScriptMotor support dynamically on windows (win32-x86 & windows-x64)

link -nologo /subsystem:windows /dll -LTCG -incremental:no -opt:ref -release  /MACHINE:X64      -out:ScriptMotor.dll -implib:ScriptMotor.lib        ScriptMotorDriver.obj      ../../../lib/windows-x64/motor.lib N:/epics/motor-devel/support/asyn/lib/windows-x64/asyn.lib  N:\epics\motor-devel\testing\pullreq79\lua/lib/windows-x64/lua.lib  M:/epics/base-3.15.5/lib/windows-x64/dbRecStd.lib M:/epics/base-3.15.5/lib/windows-x64/dbCore.lib  M:/epics/base-3.15.5/lib/windows-x64/ca.lib  M:/epics/base-3.15.5/lib/windows-x64/Com.lib
   Creating library ScriptMotor.lib and object ScriptMotor.exp
ScriptMotorDriver.obj : error LNK2001: unresolved external symbol lua_pcallk
ScriptMotorDriver.obj : error LNK2001: unresolved external symbol lua_rotate
ScriptMotorDriver.obj : error LNK2001: unresolved external symbol lua_type
ScriptMotorDriver.obj : error LNK2001: unresolved external symbol luaL_newstate
ScriptMotorDriver.obj : error LNK2001: unresolved external symbol lua_setglobal
ScriptMotorDriver.obj : error LNK2001: unresolved external symbol "void __cdecl luaLoadMacros(struct lua_State *,char const *)" (?luaLoadMacros@@YAXPEAUlua_State@@PEBD@Z)
ScriptMotorDriver.obj : error LNK2001: unresolved external symbol lua_pushboolean
ScriptMotorDriver.obj : error LNK2001: unresolved external symbol lua_tolstring
ScriptMotorDriver.obj : error LNK2001: unresolved external symbol lua_getglobal
ScriptMotorDriver.obj : error LNK2001: unresolved external symbol lua_pushstring
ScriptMotorDriver.obj : error LNK2001: unresolved external symbol lua_tonumberx
ScriptMotorDriver.obj : error LNK2001: unresolved external symbol lua_pushnumber
ScriptMotorDriver.obj : error LNK2001: unresolved external symbol "int __cdecl luaLoadScript(struct lua_State *,char const *)" (?luaLoadScript@@YAHPEAUlua_State@@PEBD@Z)
ScriptMotorDriver.obj : error LNK2001: unresolved external symbol lua_toboolean
ScriptMotorDriver.obj : error LNK2001: unresolved external symbol lua_settop
ScriptMotor.dll : fatal error LNK1120: 15 unresolved externals
M:/epics/base-3.15.5/configure/RULES_BUILD:284: recipe for target 'ScriptMotor.dll' failed
make[3]: *** [ScriptMotor.dll] Error 1120
make[3]: Leaving directory 'N:/epics/motor-devel/testing/pullreq79/motor/motorApp/ScriptMotorSrc/O.windows-x64'
M:/epics/base-3.15.5/configure/RULES_ARCHS:58: recipe for target 'install.windows-x64' failed
make[2]: *** [install.windows-x64] Error 2
make[2]: Leaving directory 'N:/epics/motor-devel/testing/pullreq79/motor/motorApp/ScriptMotorSrc'
M:/epics/base-3.15.5/configure/RULES_DIRS:84: recipe for target 'ScriptMotorSrc.install' failed
make[1]: *** [ScriptMotorSrc.install] Error 2
make[1]: Leaving directory 'N:/epics/motor-devel/testing/pullreq79/motor/motorApp'
M:/epics/base-3.15.5/configure/RULES_DIRS:84: recipe for target 'motorApp.install' failed
make: *** [motorApp.install] Error 2

@keenanlang
Copy link
Member Author

Just updated the Lua module to fix windows shared library functionality, after pulling from master was able to compile on my machine.

@kmpeters
Copy link
Member

kmpeters commented May 2, 2018

I was able to build the ScriptMotor support against the latest version of the lua module (epics-modules/lua@58f1a16). I had to create the following files in the lua/configure directory to build for debug architectures on Windows:

CONFIG_SITE.Common.win32-x86-debug
CONFIG_SITE.Common.windows-x64-debug

@kmpeters kmpeters merged commit 53cea7f into epics-modules:master May 2, 2018
@kmpeters kmpeters added this to the R6-11 milestone May 2, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants