Demo

You can build an EPICS IOC with the steps described below.

This demo uses virtual CAN interfaces for an IOC on your system.

It also uses sumo to fetch and build all the needed device supports.

The demo shows how data is transferred from one output record via the CAN bus to one input record.

How to build

Note

If you are interested in how the demo works in detail, look at: Demo details

Download ioc-multican.tgz.

Unpack with:

tar -xzf ioc-multican.tgz

Build everything with:

cd ioc-multican
./create-ioc.sh

How to run

Note

You need ‘sudo’ permissions for this test since the IOC uses real time task priorities that are not allowed for ordinary users.

In one terminal start the ioc with:

./start-ioc.sh

In another terminal you can inspect the records with these commands:

source ./setenv.sh
camonitor ca1testo cantesto ca2testo cantesti

This shows that the records are processed in this order:

  • ca1testo : get counter, trigger can write

  • cantesto : write to CAN bus, trigger ca2testo

  • ca2testo : processed after CAN write

  • cantesti : read from CAN bus, this comes last

You can also see timestamps with:

source ./setenv.sh
camonitor cantesto_ts.VALA cantesti_ts.VALA

This shows the human readable microsecond timestamp of the CAN write and CAN read object.

With the ‘candump’ utility you can see your CAN bus I/O with:

candump any

What to do next

You may extend the database file ‘socanTest.db’ with other records and for example, the SDO protocol.

Demo details

The demo uses sumo to build MultiCAN and all supports that it depends on. sumo has a dependency database that contains information where to get an EPICS support and on which other supports it depends.

The demo script creates a first version of a dependency database with command sumo config new and then adds definitions for MultiCAN and the support modules MultiCAN needs.

Here are the relevant parts of the dependency database:

{
    "ALARM": {
        "R4-5": {
            "aliases": {
                "BASE": "EPICS_BASE",
                "MISC_DBC": "DBC"
            },
            "dependencies": [
                "BASE",
                "MISC_DBC"
            ],
            "source": {
                "git": {
                    "tag": "R4-5",
                    "url": "git://git.code.sf.net/p/almlib-hzb/code"
                }
            }
        }
    },
    "BASE": {
        "R3-15-9": {
            "make-recipes": {
                "config": [
                    "echo \"export EPICS_HOST_ARCH=$$(perl $(BASE)/src/tools/EpicsHostArch.pl)\" > $DIR/setenv.sh",
                    "echo \"export EPICS_BASE=$(BASE)\" >> $DIR/setenv.sh",
                    "echo \"export PATH=\\$$EPICS_BASE/bin/\\$$EPICS_HOST_ARCH:\\$$PATH\" >> $DIR/setenv.sh"
                ],
                "distclean": [
                    "$(MAKE) -C $DIR distclean",
                    "rm -f $DIR/setenv.sh"
                ]
            },
            "source": {
                "git": {
                    "tag": "R3.15.9",
                    "url": "https://github.com/epics-base/epics-base.git"
                }
            }
        }
    },
    "BSPDEP_TIMER": {
        "R6-11": {
            "aliases": {
                "BASE": "EPICS_BASE"
            },
            "dependencies": [
                "BASE"
            ],
            "source": {
                "git": {
                    "tag": "R6-11",
                    "url": "git://git.code.sf.net/p/bspdep-timer-hzb/code"
                }
            }
        }
    },
    "MCAN": {
        "R2-9-3-Linux": {
            "aliases": {
                "BASE": "EPICS_BASE"
            },
            "dependencies": [
                "ALARM",
                "BASE",
                "MISC_DBC",
                "SOCAN",
                "SOFT_DEVHWCLIENT",
                "TOOLS_HGEN"
            ],
            "make-recipes": {
                "config": [
                    "cd $DIR && sed -i -e 's#^\\(USE_SCAN_Linux\\) *=.*#\\1=NO#;s#^\\(USE_SOCAN_Linux\\) *=.*#\\1=YES#' configure/CONFIG"
                ]
            },
            "source": {
                "git": {
                    "tag": "R2-9-3",
                    "url": "git://git.code.sf.net/p/mcan/code"
                }
            }
        }
    },
    "MISC_DBC": {
        "R3-1": {
            "aliases": {
                "BASE": "EPICS_BASE"
            },
            "dependencies": [
                "BASE"
            ],
            "source": {
                "git": {
                    "tag": "R3-1",
                    "url": "git://git.code.sf.net/p/misc-dbc-hzb/code"
                }
            }
        }
    },
    "SOCAN": {
        "1.0.1": {
            "make-recipes": {
                "all": [
                    "$(MAKE) -C $DIR epics"
                ]
            },
            "source": {
                "hg": {
                    "tag": "1.0.1",
                    "url": "http://hg.code.sf.net/p/socan/code"
                }
            }
        }
    },
    "SOFT_DEVHWCLIENT": {
        "R3-1": {
            "aliases": {
                "BASE": "EPICS_BASE"
            },
            "dependencies": [
                "BASE"
            ],
            "source": {
                "git": {
                    "tag": "R3-1",
                    "url": "git://git.code.sf.net/p/soft-devhwclient-hzb/code"
                }
            }
        }
    },
    "TOOLS_HGEN": {
        "1.8": {
            "source": {
                "hg": {
                    "tag": "1.8",
                    "url": "http://hg.code.sf.net/p/hgen/code"
                }
            }
        }
    }
}

The demo script then creates an EPICS ioc with makeBaseApp.pl.

The created directory tree is then modified for the demo application.

File configure/MODULES defines what support modules the application needs, this is the content of this file:

{
    "alias": [
        "BASE:EPICS_BASE",
        "BSPDEP_TIMER:TIMER",
        "SOFT_DEVHWCLIENT:DEVHWCLIENT"
    ],
    "module": [
        "ALARM:R4-5",
        "BASE:R3-15-9",
        "BSPDEP_TIMER:R6-11",
        "MCAN:R2-9-3-Linux",
        "SOCAN:1.0.1",
        "MISC_DBC:R3-1",
        "SOFT_DEVHWCLIENT:R3-1",
        "TOOLS_HGEN:1.8"
    ]
}

The file socanTestApp/src/Makefile contains these statements to use MultiCAN:

socanTest_DBD += alm.dbd
socanTest_DBD += hwLowcalRecord.dbd
socanTest_DBD += MultiCAN.dbd
socanTest_DBD += socan.dbd

socanTest_LIBS += socan sci
socanTest_LIBS += alm
socanTest_LIBS += hwLowcalRecord mCANSupport mCANCore

The startup script iocBoot/iocsocanTest/st.cmd initializes the application. Here are the relevant parts for the usage of MultiCAN:

# initialize almlib:
alm_init()

# Initialize MCAN:
mcanInit()
gpsInit()
dbg_init()

# Set initHook for MultiCAN:
mCANRegisterInitHooks()

# Enable handshake in LowCAL:
var glbl_lowcal_use_handshake 1

# Override inhibit time with 0 (microseconds):
var glbl_lowcal_override_inhibit 0

# Print messages for errors in GPS layer:
var gpsPrintErrors 0

# init socan
socantest("abc")
#socan_tracelevel(2)
socan_tracelevel(0)
socan_errprintlevel(3)
socan_add_port("vcan0")
socan_add_port("vcan1")
socan_add_port("vcan2")
socan_add_port("vcan3")
socan_init()

All calls to *_init functions up to var gpsPrintErrors 0 are needed to initialize MultiCAN.

The commands below # init socan initialize the socan support that is used by MultiCAN.

socan_tracelevel(0) defines that socan doean’t print trace output.

socan_errprintlevel(3) defines that even minor errors are printed to the console.

socan_add_port(INTERFACE) defines which interface maps to which port number, staring implicitly with port 0. So “vcan0” maps to port 0, “vcan1” to port 1 and so on.

socan_init() initializes the socan module.

The database definition file socanTestApp/Db/socanTest.db defines some records. We show here the two CAN bus records. Both use the ‘lowCAL’ protocol:

record(longout,"cantesto")
# write to CAN bus:
#     variable-type: server basic write-only,
#     data-type: unsigned long, length: 4 bytes
#     port: 0, out-cob: 2, in-cob: 0,
#     inhibit: 1.0 [ms], timeout: 1000 [ms]
{
  field(DTYP,"lowcal")
  field(OUT,"@b L 4 0 2 0 0 a 3e8 0")
  field(DOL, "counter NPP NMS")
  field(OMSL, "closed_loop")
  field(FLNK, "ca2testo")
}

record(longin,"cantesti")
# read from the CAN bus
#     variable-type: server basic write-only,
#     data-type: unsigned long, length: 4 bytes,
#     port: 1, out-cob: 0, in-cob: 2,
#     inhibit: 1.0 [ms], timeout: 1000 [ms],
{
  field(DTYP,"lowcal")
  field(INP,"@h L 4 1 0 2 0 a 3e8 0")
  field(SCAN,"I/O Intr")
  field(FLNK, "cantesto_ts")
}

The demo script then compiles the IOC and initializes the virtual CAN interfaces with the scripts init-can.sh and link-vcan-devices.sh that are part of the socan device support.

Finally here is the complete file create-ioc.sh:

#!/bin/bash

SCRIPT_FULL_NAME=$(readlink -e $0)
MYDIR=$(dirname $SCRIPT_FULL_NAME)
MYNAME=$(basename $SCRIPT_FULL_NAME)

cd "$MYDIR" || exit 1

echo "Creating virtual environment for python."
python -m venv VENV

echo "Activate virtual environment."
source VENV/bin/activate

echo "Installing sumo."
pip install epics-sumo

echo "Creating sumo.config."
sumo config new SUMO github
sumo config make sumo.config --yes --#opt-preload configure/MODULES --#opt-preload configure/MODULES.HOST

echo "Add additional sumo dependency database entries for MultiCAN."
sumo db merge templates/EXTRA.DB

echo "Building EPICS base, please be patient..."
sleep 1
sumo build new BASE:R3-15-9 --makeflags "-sj" --progress

# define BASE environment variable:
eval "$(sumo build use BASE:R3-15-9 -o - | sed -e 's/^EPICS_//')"

echo "EPICS Base is in $BASE."

# set environment variables to use that EPICS base:
source $BASE/setenv.sh

echo "Creating ioc with makeBaseApp.pl."
makeBaseApp.pl -b $EPICS_BASE -t ioc socanTest
makeBaseApp.pl -i -t ioc -p socanTest iocsocanTest

echo "Configure IOC for MultiCAN."
cp -a templates/MODULES configure
cp -a templates/Db/* socanTestApp/Db
cp -a templates/src/* socanTestApp/src
cp -a templates/start-ioc.sh .
cp -a templates/boot/* iocBoot/iocsocanTest

echo "Building all support modules, please be patient."
sleep 1
sumo build new --makeflags "-sj" --progress

echo "Use support modules in ioc application."
sumo build use

echo "Now build the ioc."
make -sj

# define SOCAN environment variable:
eval "$(grep SOCAN configure/RELEASE)"

echo "Define virtual CAN interfaces, this requires sudo rights."
$SOCAN/bin/init-can.sh -d vcan

echo "Link first two CAN interfaces."
$SOCAN/bin/link-vcan-devices.sh link 0 1
echo

echo
echo "source $MYDIR/VENV/bin/activate" > setenv.sh
echo "eval \"\$(sumo help completion-line)\"" >> setenv.sh
echo "BASE=$BASE" >> setenv.sh
echo "source \$BASE/setenv.sh" >> setenv.sh
echo
echo "Finished, you may now start the ioc (requires sudo) with:"
echo "./start-ioc.sh"
echo
echo "In a second window you may enter:"
echo "  source ./setenv.sh"
echo "And then run for example:"
echo
echo "  camonitor ca1testo cantesto ca2testo cantesti"
echo
echo "This shows that the records are processed in the order:"
echo "  ca1testo : get counter, trigger can write"
echo "  cantesto : write to CAN bus, trigger ca2testo"
echo "  ca2testo : processed after CAN write"
echo "  cantesti : read from CAN bus, this comes last"
echo
echo "Second example: get timestamps, enter:"
echo "  camonitor cantesto_ts.VALA cantesti_ts.VALA"
echo
echo "This shows the human readable microsecond timestamp of"
echo "the CAN write and CAN read object."