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-1.1.tgz.

Unpack with:

tar -xzf ioc-multican-1.1.tgz

Build everything with:

cd ioc-multican-1.1
./create-ioc.sh

How to run

Note

You need ‘sudo’ permissions for this test since the IOC uses real time task priorities that is 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.

Instead of the virtual CAN bus interfaces from this example you may want to use physical CAN bus interfaces of CAN Bus I/O card. To do this, replace in file ‘iocBoot/iocsocanTest/st.cmd’ the names “vcan0” to “vcan3” with “can0” to “can3”.

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 dependency database with command sumo config new sumo-free-database INITIAL.

It then creates a file ‘configure/MODULES’ with command sumo build complete.

Here are the relevant parts of the dependency database:

{
  "ALARM": {
      "R4-5-1": {
          "aliases": {
              "BASE": "EPICS_BASE",
              "MISC_DBC": "DBC"
          },
          "dependencies": [
              "BASE",
              "MISC_DBC"
          ],
          "source": {
              "git": {
                  "tag": "R4-5-1",
                  "url": "https://git.code.sf.net/p/almlib-hzb/code"
              }
          }
      }
  },
  "BASE": {
      "R3-15-9": {
          "make-recipes": {
              "config": [
                  "sed -i 's/rhs\\.pConstEntry/rhs.pEntry/' $DIR/src/libCom/cxxTemplates/tsSLList.h",
                  "sed -i '/^STATIC_LDFLAGS_YES/i POSIX_CFLAGS+= -std=gnu17' $DIR/configure/os/CONFIG.linux-x86.linux-x86",
                  "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"
              }
          }
      },
    },
    "MCAN": {
        "R2-10-2-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-10-2",
                    "url": "https://git.code.sf.net/p/mcan/code"
                }
            }
        }
    },
    "MISC_DBC": {
          "R3-1-1": {
            "aliases": {
                "BASE": "EPICS_BASE"
            },
            "dependencies": [
                "BASE"
            ],
            "source": {
                "git": {
                    "tag": "R3-1-1",
                    "url": "https://git.code.sf.net/p/misc-dbc-hzb/code"
                }
            }
        }
    },
    "SOCAN": {
        "1.2.1": {
            "make-recipes": {
                "all": [
                    "$(MAKE) -C $DIR epics"
                ]
            },
            "source": {
                "hg": {
                    "tag": "1.2.1",
                    "url": "http://hg.code.sf.net/p/socan/code"
                }
            }
        }
    },
    "SOFT_DEVHWCLIENT": {
        "R3-1-1": {
            "aliases": {
                "BASE": "EPICS_BASE"
            },
            "dependencies": [
                "BASE"
            ],
            "source": {
                "git": {
                    "tag": "R3-1-1",
                    "url": "https://git.code.sf.net/p/soft-devhwclient-hzb/code"
                }
            }
        }
    },
    "TOOLS_HGEN": {
        "1.8.1": {
            "make-recipes": {
                "all": [
                    "$(MAKE) -C $DIR epics"
                ]
            },
            "source": {
                "hg": {
                    "tag": "1.8.1",
                    "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",
        "SOFT_DEVHWCLIENT:DEVHWCLIENT"
    ],
    "module": [
        "ALARM:R4-5-1",
        "BASE:R3-15-9",
        "MCAN:R2-10-2-Linux",
        "MISC_DBC:R3-1-1",
        "SOCAN:1.2.1",
        "SOFT_DEVHWCLIENT:R3-1-1",
        "TOOLS_HGEN:1.8.1"
    ]
}

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.