114 Commits

Author SHA1 Message Date
0fb3099f3d use ecmc 11.0 2025-10-13 11:59:47 +02:00
9c77ecd894 epics 7.0.9 2025-04-22 12:23:26 +02:00
9992bc0787 Add build for deb12 2025-03-21 11:36:21 +01:00
0b6092446a Update makefile 2025-03-03 15:25:00 +01:00
3f89bf94c7 Prep for ecmc v10 2024-12-16 10:43:47 +01:00
5e10e7c929 New plugin concept 2024-12-16 09:24:16 +01:00
Anders Sandström
6d7f2ef37e Increase sdo timeout 2022-08-25 15:29:39 +02:00
Anders Sandström
154c6fb410 Fix mutex unlock 2022-08-25 08:09:14 +02:00
anderssandstrom
c187fbc008 Update README.md 2021-04-05 18:46:40 +02:00
anderssandstrom
b5b0af71c3 Update README.md 2021-04-05 18:45:44 +02:00
anderssandstrom
eb381156e3 Update README.md 2021-03-25 14:47:11 +01:00
anderssandstrom
ed83287447 Update readme.md 2021-03-25 14:46:48 +01:00
anderssandstrom
ed103c6a9e Update README.md 2021-03-25 14:46:02 +01:00
anderssandstrom
134ea6f646 Update README.md 2021-03-25 14:45:32 +01:00
anderssandstrom
531be5dc92 Update README.md 2021-03-25 14:45:12 +01:00
anderssandstrom
034abc827b Update README.md 2021-03-25 14:44:51 +01:00
anderssandstrom
334400afce Update README.md 2021-03-25 14:43:08 +01:00
anderssandstrom
c18e6d756d Update readme.md 2021-03-25 14:41:20 +01:00
anderssandstrom
b1a35993f5 Update readme.md 2021-03-24 12:32:10 +01:00
anderssandstrom
8e0a5d774c Update README.md 2021-03-24 12:29:44 +01:00
anderssandstrom
bff88f1722 Update readme.md 2021-03-24 12:28:44 +01:00
anderssandstrom
86e9ce7c6c Update README.md 2021-03-24 12:27:56 +01:00
anderssandstrom
16034c550a Update readme.md 2021-03-24 12:23:54 +01:00
anderssandstrom
9916c3bb9e Update readme.md 2021-03-24 12:21:23 +01:00
anderssandstrom
d2a58cb762 Update readme.md 2021-03-24 12:18:57 +01:00
anderssandstrom
5ff3d6c7fd Update readme.md 2021-03-24 12:15:01 +01:00
anderssandstrom
d7a02309bb Update readme.md 2021-03-24 12:09:20 +01:00
Anders Sandstrom
fca81993e9 Kvaser: Rename driver sources and update readme 2021-03-24 12:07:33 +01:00
Anders Sandstrom
686f6fdf97 Add some notes on the kvaser 3.10 socketcan driver that is not working. 2021-03-24 11:59:05 +01:00
anderssandstrom
eda35ba589 Update README.md 2021-03-24 10:46:16 +01:00
Anders Sandstrom
d8b89f9247 Merge branch 'master' of https://github.com/anderssandstrom/e3-ecmc_plugin_socketcan 2021-03-24 10:42:34 +01:00
anderssandstrom
6061a168ff Update readme.md 2021-03-24 10:40:51 +01:00
anderssandstrom
ab2ff7193f Update readme.md 2021-03-24 10:33:57 +01:00
Anders Sandstrom
15d0007186 Add can_change_state() to driver (from kvaser support). 2021-03-24 10:30:42 +01:00
anderssandstrom
2d7a0fadac Update README.md 2021-03-24 10:03:02 +01:00
anderssandstrom
bcb6599527 Update README.md 2021-03-24 10:02:03 +01:00
Anders Sandstrom
d1cc5a4d8d Update readme with install instructions 2021-03-24 09:47:20 +01:00
Anders Sandstrom
256f0ef749 Improve error printouts for read and write 2021-03-24 09:46:46 +01:00
anderssandstrom
14cc799609 Update README.md 2021-03-23 11:22:15 +01:00
anderssandstrom
a7e9cb5782 Update README.md 2021-03-23 11:21:37 +01:00
anderssandstrom
a63bbd1ab6 Update readme.md 2021-03-23 11:21:16 +01:00
anderssandstrom
f6d37e9b1f Update readme.md 2021-03-23 11:20:48 +01:00
anderssandstrom
af98edf6af Update README.md 2021-03-23 11:20:24 +01:00
anderssandstrom
3e95098790 Update readme.md 2021-03-23 11:19:35 +01:00
anderssandstrom
b89533100f Update readme.md 2021-03-23 11:13:06 +01:00
anderssandstrom
82250a4c90 Update readme.md 2021-03-23 11:12:39 +01:00
anderssandstrom
d34f6fe413 Update readme.md 2021-03-23 11:11:49 +01:00
anderssandstrom
79a77c1375 Update README.md 2021-03-23 11:10:50 +01:00
anderssandstrom
985ba27bd3 Update readme.md 2021-03-23 11:07:06 +01:00
Anders Sandstrom
dc9041f4a2 Update README 2021-03-23 11:01:49 +01:00
anderssandstrom
9f2e406899 Update README.md 2021-03-22 15:48:52 +01:00
Anders Sandstrom
d82c81e4ac Add some error handling 2021-03-22 14:35:28 +01:00
Anders Sandstrom
360bd8fe28 Only read after connected.. 2021-03-22 13:27:25 +01:00
anderssandstrom
58d8467af8 Update readme.md 2021-03-22 12:32:06 +01:00
Anders Sandstrom
36bd49184d Update RELEASE and CONFIG_DEV 2021-03-22 12:12:58 +01:00
Anders Sandstrom
0eae0d9136 Update readme for install of kvaser driver 2021-03-22 12:06:56 +01:00
Anders Sandstrom
3995f1db17 Add dev NMT heartbeat timeout to devices. 2021-03-22 10:57:04 +01:00
Anders Sandstrom
682cb02962 Merge branch 'master' of https://github.com/anderssandstrom/e3-ecmc_plugin_socketcan 2021-03-22 10:24:15 +01:00
Anders Sandstrom
2d42a0d0b9 Add device NMT asyn param. Some cleanup 2021-03-22 10:23:56 +01:00
anderssandstrom
53bf9fb861 Update readme.md 2021-03-18 20:01:44 +01:00
anderssandstrom
2d5c5067bf Update readme.md 2021-03-18 19:59:19 +01:00
anderssandstrom
b77c4bfa24 Update readme.md 2021-03-18 19:54:55 +01:00
anderssandstrom
19bf3f7a32 Update readme.md 2021-03-18 19:41:26 +01:00
Anders Sandstrom
f7839fc6a8 add kvaser readme 2021-03-18 17:18:53 +01:00
anderssandstrom
99f945a04b Add files via upload 2021-03-18 17:17:59 +01:00
anderssandstrom
fd1b4bb9ef Update README.md 2021-03-18 14:38:18 +01:00
Anders Sandstrom
d289e89ab3 Also refresh SDO in not DBG mode.. Bugfix 2021-03-18 13:06:29 +01:00
Anders Sandstrom
a2ff30f305 Remove some printfs 2021-03-18 12:46:51 +01:00
Anders Sandstrom
f3b8a2a059 Remove pmu905 related files (moved to pmu905 repo) 2021-03-18 10:23:41 +01:00
Anders Sandstrom
a2a950f4de Add info about asubrecord in README.md 2021-03-18 10:15:10 +01:00
Anders Sandstrom
ad0b4f873e Cleanup in asub 2021-03-18 10:10:48 +01:00
Anders Sandstrom
46679dfc3e Rename asub and cleanup 2021-03-18 10:01:35 +01:00
Anders Sandstrom
5b518a0661 Remove submodules and soem cleanup 2021-03-18 09:24:55 +01:00
Anders Sandstrom
76f01227c0 Merge branch 'master' of https://github.com/anderssandstrom/e3-ecmc_plugin_socketcan 2021-03-17 15:57:36 +01:00
Anders Sandstrom
7f2ef1d04d WIP basic config. Add asub.. 2021-03-17 15:57:16 +01:00
anderssandstrom
1a4c4a881f Add files via upload 2021-03-17 15:00:58 +01:00
Anders Sandstrom
54949701d1 WIP on basic config. Amplifier ON works.. WIP 2021-03-16 16:53:29 +01:00
Anders Sandstrom
10e2935483 Use uchar for basic config sdo 2021-03-16 15:49:07 +01:00
Anders Sandstrom
700d63de97 Some cleanup 2021-03-16 13:53:07 +01:00
Anders Sandstrom
90f33f1200 Use calc records for scalings 2021-03-16 13:30:05 +01:00
Anders Sandstrom
d989b4c737 WIP 2021-03-16 13:17:39 +01:00
Anders Sandstrom
c462c38e77 Correct status source 2021-03-16 11:56:27 +01:00
Anders Sandstrom
9a21232296 Test on real unit 2021-03-16 11:53:37 +01:00
anderssandstrom
5409f8e7f7 Update README.md 2021-03-12 09:10:39 +01:00
Anders Sandstrom
d5bf120339 Add some info in readme 2021-03-12 09:05:56 +01:00
Anders Sandstrom
111f6f460a SDO: Change read timeout to read sample time 2021-03-12 09:05:41 +01:00
Anders Sandstrom
7fdd51b0cd Add PDO output template. 2021-03-11 17:05:01 +01:00
Anders Sandstrom
e5f696a1eb SDO write tested. 2021-03-11 16:59:52 +01:00
Anders Sandstrom
1b88210a60 Add some test pvs for pdos and sdos 2021-03-10 17:06:31 +01:00
Anders Sandstrom
5e6ed01cd7 Add some more SDO pv:s. 2021-03-10 16:37:30 +01:00
Anders Sandstrom
188f05b8a1 Add template for CAN SDO 2021-03-10 16:26:45 +01:00
Anders Sandstrom
74b527f306 Fix buffer allocation. 2021-03-10 16:26:13 +01:00
Anders Sandstrom
cb9fe2a361 Add comments 2021-03-10 15:42:02 +01:00
Anders Sandstrom
6e84d4bf0c Add write() error handling. 2021-03-10 15:35:10 +01:00
Anders Sandstrom
8ab585652e Update asynparams in ecmc_rt thread (execute()) 2021-03-10 15:27:21 +01:00
Anders Sandstrom
4e0ac9462a Cleanup 2021-03-10 14:45:42 +01:00
Anders Sandstrom
fa62fbf918 Add asyn params for pdo, add write callbacks for sdo and pdo. 2021-03-10 14:37:40 +01:00
Anders Sandstrom
0b7954e478 Add asyn param for data of SDO. 2021-03-10 12:02:41 +01:00
Anders Sandstrom
4361b083c5 Rename some funcs.. 2021-03-10 11:04:04 +01:00
Anders Sandstrom
020d7932af Lock seems to work. Add more SDO:s to testscript to test lock 2021-03-10 10:33:03 +01:00
Anders Sandstrom
d90918ac9e Add lock and sync of SDO1 2021-03-10 10:23:39 +01:00
Anders Sandstrom
8f5ea3acd9 Try to sync SDO, WIP 2021-03-09 16:51:34 +01:00
Anders Sandstrom
e5e754f5e8 Add pdo index to pmu905.script 2021-03-09 14:57:07 +01:00
Anders Sandstrom
c42c284b00 Add pmu905.script 2021-03-09 14:53:08 +01:00
Anders Sandstrom
78d6cd5a12 Some cleanup. 2021-03-09 14:30:57 +01:00
Anders Sandstrom
afb93a2b00 Add LSS, SYNC and heartbeat sample time for master 2021-03-09 14:24:20 +01:00
Anders Sandstrom
399023e023 Add SDO and PDO iocsh commands 2021-03-09 13:47:02 +01:00
Anders Sandstrom
02d9ef3244 Start to add iocsh cmds.. 2021-03-09 10:26:28 +01:00
Anders Sandstrom
e54cca6261 Merge branch 'master' of https://github.com/anderssandstrom/e3-ecmc_plugin_socketcan 2021-03-09 09:23:38 +01:00
Anders Sandstrom
4d4dd047c2 Add master and some test objetcs. 2021-03-09 09:23:21 +01:00
Anders Sandstrom
2ece5fd754 Add master, add some test stuff.. 2021-03-09 09:21:55 +01:00
Anders Sandstrom
00fb8e4c81 Add build config 2021-03-08 17:04:28 +01:00
Anders Sandstrom
1bd8d56f1f Add canopen device 2021-03-08 17:04:03 +01:00
Anders Sandstrom
a6456894ae PDO: Add mutex 2021-03-08 17:03:43 +01:00
53 changed files with 4106 additions and 1205 deletions

3
.gitignore vendored
View File

@@ -14,4 +14,5 @@ core.*
*PVs.list
*-loc/*.Makefile
ecmc_plugin_fft/*.Makefile
*__*
*__*
O.*

9
.gitmodules vendored
View File

@@ -1,9 +0,0 @@
[submodule "ecmc_plugin_socketcan/ecmc_plugin_socketcanApp/src/CANopenNode"]
path = ecmc_plugin_socketcan/ecmc_plugin_socketcanApp/src/CANopenNode
url = https://github.com/anderssandstrom/CANopenNode.git
[submodule "ecmc_plugin_socketcan/ecmc_plugin_socketcanApp/src/CANopenSocket"]
path = ecmc_plugin_socketcan/ecmc_plugin_socketcanApp/src/CANopenSocket
url = https://github.com/anderssandstrom/CANopenSocket
[submodule "ecmc_plugin_socketcan/ecmc_plugin_socketcanApp/src/openCANopen"]
path = ecmc_plugin_socketcan/ecmc_plugin_socketcanApp/src/openCANopen
url = https://github.com/marel-keytech/openCANopen

45
GNUmakefile Normal file
View File

@@ -0,0 +1,45 @@
include /ioc/tools/driver.makefile
MODULE = ecmc_plugin_socketcan
# "Transfer" module name to plugin
USR_CFLAGS +=-DECMC_PLUGIN_MODULE_NAME=${MODULE}
BUILDCLASSES = Linux
ARCH_FILTER = deb10% deb12%
# Run 7.0.6 for now
EXCLUDE_VERSIONS+=3 7.0.5 7.0.6 7.0.7
IGNORE_MODULES += asynMotor
IGNORE_MODULES += motorBase
USR_CXXFLAGS += -std=c++17
OPT_CXXFLAGS_YES = -O3
# dependencies
ecmc_VERSION = 11.0
APPDB:=db
APPSRC:=src
USR_CFLAGS += -shared -fPIC -Wall -Wextra
USR_CPPFLAGS += -std=c++11
USR_LDFLAGS += -lstdc++
USR_INCLUDES += -I$(where_am_I)$(APPSRC)
TEMPLATES += $(wildcard $(APPDB)/*.db)
TEMPLATES += $(wildcard $(APPDB)/*.template)
TEMPLATES += $(wildcard $(APPDB)/*.substitutions)
SOURCES += $(APPSRC)/ecmcPluginSocketCAN.c
SOURCES += $(APPSRC)/ecmcSocketCAN.cpp
SOURCES += $(APPSRC)/ecmcSocketCANWrap.cpp
SOURCES += $(APPSRC)/ecmcSocketCANWriteBuffer.cpp
SOURCES += $(APPSRC)/ecmcCANOpenSDO.cpp
SOURCES += $(APPSRC)/ecmcCANOpenPDO.cpp
SOURCES += $(APPSRC)/ecmcCANOpenDevice.cpp
SOURCES += $(APPSRC)/ecmcCANOpenMaster.cpp
SOURCES += $(APPSRC)/ecmcByteToArrayAsub.cpp
DBDS += $(APPSRC)/ecmcSocketCAN.dbd

486
README.md
View File

@@ -1,3 +1,489 @@
e3-ecmc_plugin_socketcan
======
ESS Site-specific EPICS module : ecmcPlugin_socketcan
# SocketCAN
This module adds some CAN support to ecmc based on SocketCAN:
https://en.wikipedia.org/wiki/SocketCAN
## Hardware
This module have been tested with Kvaser Leaf Light v2 usb can interface.
More info about how to setup this hardware can be found here:
[Kvaser Leaf V2 setup](kvaser/readme.md)
# Install ecmc plugin module
## Dependencies
This module have dependencies to:
1. ecmc
3. asyn
## Configuration
Ensure that these two files are correct:
1. configure/CONFIG_MODULE:
```
EPICS_MODULE_TAG:=master
E3_MODULE_VERSION:=master
ECMC_DEP_VERSION:=6.3.2
ASYN_DEP_VERSION:=4.41.0
```
2. configure/RELEASE:
```
EPICS_BASE:=${HOME}/epics/base-7.0.4
E3_REQUIRE_VERSION:=3.4.0
```
## Install
The module is installed like any other e3 module by "make install":
```
make install
```
Now the module should be ready for use.
## Load plugin into ecmc
Like all ecmc plugins the plugin must be loaded into ecmc with the "loadPlugin.cmd" command.
The loading is needed so that ecmc can link to the callbacks, plc functions and plc constants in the plugin.
The "loadPlugin.cmd" command basically takes three arguments:
1. PLUGIN_ID: This is just an index of the different plugins loaded into ecmc. So the first loaded plugin should have index 0.
2. FILE: Path to the "libecmc_plugin_socketcan.so" file.
3. CONFIG: Plugin configuration string. See below for details
### Configurations string "CONFIG"
The configuration string contains startup configs for the plugin. Currentlly the below configurations can be specified in the CONFIG string:
```
DBG_PRINT=<1/0> : Enables/disables printouts from plugin, default = disabled (=0).
IF=<if name> : Sets can interface (example: can0, vcan0..).
CONNECT=<1/0> : Auto connect to if at startup, default = autoconnect (=1).
```
Also see "Plugin" chapter below for more information about the plugin.
### Example
Example of loading the plugin connecting to the "can0" interface:
```
## Load plugin:
epicsEnvSet(ECMC_PLUGIN_FILNAME,"${HOME}/epics/base-7.0.4/require/${E3_REQUIRE_VERSION}/siteMods/ecmc_plugin_socketcan/master/lib/${EPICS_HOST_ARCH=linux-x86_64}/libecmc_plugin_socketcan.so")
epicsEnvSet(ECMC_PLUGIN_CONFIG,"IF=can0;DBG_PRINT=0;")
${SCRIPTEXEC} ${ecmccfg_DIR}loadPlugin.cmd, "PLUGIN_ID=0,FILE=${ECMC_PLUGIN_FILNAME},CONFIG='${ECMC_PLUGIN_CONFIG}', REPORT=1"
epicsEnvUnset(ECMC_PLUGIN_FILNAME)
epicsEnvUnset(ECMC_PLUGIN_CONFIG)
```
A complete example can be found in the pmu905 repo where a R&S amplifier is interfaced using this module:
https://github.com/anderssandstrom/ecmc_pmu905_can
### Access to templates
In order to access the templates in the plugin a "require" is needed in the startup script:
```
require ecmc_plugin_socketcan master
```
# CANOpen
This module implements a few of the CANOpen functionalities (but it is far from a full CANOpen Implementation).
The current implemented features are:
* Simple master:
* LSS (basic)
* NMT (Heartbeat, dev state)
* Sync
* Simple generic (slave) device
* SDO segmented r/w
* PDO r/w
These functionalities are configured through iocsh cmds:
* ecmcCANOpenAddMaster
* ecmcCANOpenAddDevice
* ecmcCANOpenAddSDO
* ecmcCANOpenAddPDO
## ecmcCANOpenAddMaster
Issueing "ecmcCANOpenAddMaster -h" in the iocsh you will get the following help printout:
```
ecmcCANOpenAddMaster -h
Use ecmcCANOpenAddMaster(<name>, <node id>,....)
<name> : Name of master device.
<node id> : CANOpen node id of master.
<LSS sample time ms> : Sample time for LSS.
<Sync sample time ms> : Sample time for SYNC.
<Heartbeat sample time ms> : Sample time for Heartbeat.
```
Example to add a master with LSS, sync, heartbeat sample time of 1s:
```
ecmcCANOpenAddMaster("ecmcCANOpenMaster",0,1000,1000,1000)
# Load status records for device
dbLoadRecords(ecmcPluginSocketCAN_Dev.template, "P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1,CH_ID=00,DEV_ID=0")
```
Note:: You can only have one master in each ioc (one call to "ecmcCANOpenAddMaster").
## ecmcCANOpenAddDevice
Issueing "ecmcCANOpenAddDevice -h" in the iocsh you will get the following help printout:
```
ecmcCANOpenAddDevice -h
Use ecmcCANOpenAddDevice(<name>, <node id>)
<name> : Name of device.
<node id> : CANOpen node id of device.
<NMT Heartbeat timeout ms> : Timeout for NMT Heartbeat.\n")
```
Example to add a device with node id 3 and a NMT heartbeat timeout of 3s:
```
ecmcCANOpenAddDevice("testDevice",3,3000)
# Load status records for device
dbLoadRecords(ecmcPluginSocketCAN_Dev.template, "P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1,CH_ID=03,DEV_ID=3")
```
Note:: You can only use the below commands (ecmcCANOpenAddSDO and ecmcCANOpenAddODO) to nodes of an existing device or master.
## ecmcCANOpenAddSDO
Issueing "ecmcCANOpenAddSDO -h" in the iocsh you will get the following help printout:
```
ecmcCANOpenAddSDO -h
ecmcCANOpenAddSDO( in the iocsh<name>, <node id>,.....)
<name> : Name of master device.
<node id> : CANOpen node id of device/master.
<cob id tx> : CANOpen cob id of Tx of slave SDO.
<cob id rx> : CANOpen cob id of Rx of slave SDO.
<dir> : Direction 1=write and 2=read.
<ODIndex> : OD index of SDO.
<ODSubIndex> : OD sub index of SDO.
<ODSize> : OS Siz
<readSampleTimeMs>: Sample time for read in ms (write is always on demand).
```
A few examples:
```
# Example read SDO from device, will be cyclically read at an intervall of 7000ms:
ecmcCANOpenAddSDO("analogValues",3,0x583,0x603,2,0x2640,0x0,56,7000)
# Load record for SDO data
dbLoadRecords(ecmcPluginSocketCAN_SDO_input.template, "P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1,T_SMP_MS=${ECMC_SAMPLE_RATE_MS},TSE=${ECMC_TSE},NELM=${NELM=1},CH_ID=03,DEV_ID=3,suffix=AI03-Array,source=basicConfig,DTYP=asynInt8ArrayIn,FTVL=CHAR,NELM=7")
# Load record for SDO error
dbLoadRecords(ecmcPluginSocketCAN_SDO_error.template, "P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1,T_SMP_MS=${ECMC_SAMPLE_RATE_MS},TSE=${ECMC_TSE},CH_ID=03,DEV_ID=3,suffix=SDO01-AnalogValuesErr,source=analogValues")
# Example write SDO to device, will be written on demand (when write data from epics):
ecmcCANOpenAddSDO("basicConfig",3,0x583,0x603,1,0x2690,0x1,7,0)
# Load record for SDO data
dbLoadRecords(ecmcPluginSocketCAN_SDO_output.template, "P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1,T_SMP_MS=${ECMC_SAMPLE_RATE_MS},TSE=${ECMC_TSE},NELM=${NELM=1},CH_ID=03,DEV_ID=3,suffix=BasicConfig,source=basicConfig,DTYP=asynInt8ArrayOut,FTVL=CHAR,NELM=7")
# Load record for SDO error
dbLoadRecords(ecmcPluginSocketCAN_SDO_error.template, "P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1,T_SMP_MS=${ECMC_SAMPLE_RATE_MS},TSE=${ECMC_TSE},CH_ID=03,DEV_ID=3,suffix=SDO02-BasicConfigErr,source=basicConfig")
```
## ecmcCANOpenAddPDO
Issueing "ecmcCANOpenAddPDO -h" in the iocsh you will get the following help printout:
```
ecmcCANOpenAddPDO -h
Use "ecmcCANOpenAddPDO(<name>, <node id>
<name> : Name of master device.
<node id> : CANOpen node id of device/master.
<cob id> : CANOpen cob id of PDO.
<dir> : Direction 1=write and 2=read.
<ODSize> : Size of PDO (max 8 bytes).
<readTimeoutMs> : Readtimeout in ms.
<writeCycleMs> : Cycle time for write (if <= 0 then only write on change).
```
An example:
```
# Read PDO, if not recived pdo within 10s the PDO object will go into error state:
ecmcCANOpenAddPDO("status",3,0x183,2,8,10000,0)
# Load record for PDO data
dbLoadRecords(ecmcPluginSocketCAN_PDO_input.template, "P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1,T_SMP_MS=${ECMC_SAMPLE_RATE_MS},TSE=${ECMC_TSE},NELM=${NELM=1},CH_ID=03,DEV_ID=3,suffix=PDO01-Array,source=status,NELM=8")
# Load record for PDO error
dbLoadRecords(ecmcPluginSocketCAN_PDO_error.template, "P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1,T_SMP_MS=${ECMC_SAMPLE_RATE_MS},TSE=${ECMC_TSE},CH_ID=03,DEV_ID=3,suffix=PDO01-StatusErr,source=status")
```
# PVs
## Communication
The status of the CAN communication can be supervised by loading the "ecmcPluginSocketCAN_Com.template":
```
dbLoadRecords(ecmcPluginSocketCAN_Com.template, "P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1")
# Will load:
IOC_TEST:CAN-Stat-ComErr
IOC_TEST:CAN-Stat-Connected
```
The Connected Pv show if the plugin is connected to socketcan and the hardware.
The ComErr Pv shows error codes for reads and writes to socketcan socket.
## Device (also Master)
### ecmcPluginSocketCAN_Dev.template
The ecmcPluginSocketCAN_Dev.template loads a NMT PV:
```
IOC_TEST:CAN03-Stat-NMT
```
The PV tells wich state the device is in and can have the following values:
* NMT_NOT_VALID : State not valid. Something is wrong.
* NMT_BOOT_UP : Device is booting
* NMT_STOPPED : Device is stopped
* NMT_OP : Device is operational (this is the normal state when running the ioc).
* NMT_PREOP : Device is in pre operational state
## PDO
## ecmcPluginSocketCAN_PDO_input.template
The ecmcPluginSocketCAN_PDO_intput.template in used for input PDOS (data read from a device device to EPICS).
The template only contains one PV that holds the data:
Example:
```
dbLoadRecords(ecmcPluginSocketCAN_PDO_input.template, "P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1,T_SMP_MS=${ECMC_SAMPLE_RATE_MS},TSE=${ECMC_TSE},CH_ID=03,DEV_ID=3,suffix=PDO01-Status_,source=status,NELM=8")
# Loads this PV:
IOC_TEST:CAN03-PDO01-Status_
```
The data PV is an waveform of type uchar and length NELM.
## ecmcPluginSocketCAN_PDO_output.template
The ecmcPluginSocketCAN_PDO_output.template in used for output PDOS (data written from EPICS to device).
The template only contains one PV that holds the data:
Example:
```
dbLoadRecords(ecmcPluginSocketCAN_PDO_output.template, "P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1,T_SMP_MS=${ECMC_SAMPLE_RATE_MS},TSE=${ECMC_TSE},CH_ID=00,DEV_ID=0,suffix=PDO01-Reset_, source=reset,NELM=8")
# Loads this PV:
IOC_TEST:CAN03-PDO01-Reset_
```
The data PV is an waveform of type uchar and length NELM.
## ecmcPluginSocketCAN_PDO_error.template
The ecmcPluginSocketCAN_PDO_error.template in used to read the PDO error code.
Example:
```
dbLoadRecords(ecmcPluginSocketCAN_PDO_error.template, "P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1,T_SMP_MS=${ECMC_SAMPLE_RATE_MS},TSE=${ECMC_TSE},CH_ID=00,DEV_ID=0,suffix=PDO01-ResetErr,source=reset")
# Loads this PV:
IOC_TEST:CAN03-PDO01-ResetErr
```
An error could for example be that the PDO data have not been recived within the correct time frame (readTimeoutMs).
The PDO is in error state if the error code is not 0.
## SDO
## ecmcPluginSocketCAN_SDO_input.template
The ecmcPluginSocketCAN_SDO_intput.template in used for input SDOS (data read from a device device to EPICS).
The template only contains one PV that holds the data:
Example:
```
dbLoadRecords(ecmcPluginSocketCAN_SDO_input.template, "P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1,T_SMP_MS=${ECMC_SAMPLE_RATE_MS},TSE=${ECMC_TSE},CH_ID=03,DEV_ID=3,suffix=SDO01-AnalogValues_, source=analogValues,DTYP=asynInt16ArrayIn,FTVL=SHORT,NELM=28")
# Loads this PV:
IOC_TEST:CAN03-SDO01-AnalogValues_
```
The data PV is an waveform of type uchar and length NELM.
## ecmcPluginSocketCAN_SDO_output.template
The ecmcPluginSocketCAN_SDO_output.template in used for output SDOS (data written from EPICS to device).
The template only contains one PV that holds the data:
Example:
```
dbLoadRecords(ecmcPluginSocketCAN_SDO_output.template,"P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1,T_SMP_MS=${ECMC_SAMPLE_RATE_MS},TSE=${ECMC_TSE},CH_ID=03,DEV_ID=3,suffix=SDO02-BasicConfig_, source=basicConfig,DTYP=asynInt8ArrayOut,FTVL=UCHAR,NELM=7")
# Loads this PV:
IOC_TEST:CAN03-SDO02-BasicConfig_
```
The data PV is an waveform of type uchar and length NELM.
## ecmcPluginSocketCAN_SDO_error.template
The ecmcPluginSocketCAN_PSO_error.template in used to read the SDO error code.
Example:
```
dbLoadRecords(ecmcPluginSocketCAN_SDO_error.template, "P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1,T_SMP_MS=${ECMC_SAMPLE_RATE_MS},TSE=${ECMC_TSE},CH_ID=03,DEV_ID=3,suffix=SDO02-BasicConfigErr,source=basicConfig")
# Loads this PV:
IOC_TEST:CAN03-SDO02-BasicConfigErr
```
An error could for example be that the SDO data have not been recived within the correct time frame or slave is not reposning like it should.
The SDO is in error state if the error code is not 0.
# Plugin
Plugin report printout:
```
Plugin info:
Index = 0
Name = ecmcPlugin_socketcan
Description = SocketCAN plugin for use with ecmc.
Option description =
DBG_PRINT=<1/0> : Enables/disables printouts from plugin, default = disabled (=0).
IF=<if name> : Sets can interface (example: can0, vcan0..).
CONNECT=<1/0> : Auto connect to if at startup, default = autoconnect (=1).
Filename = /home/dev/epics/base-7.0.4/require/3.4.0/siteMods/ecmc_plugin_socketcan/master/lib/linux-x86_64/libecmc_plugin_socketcan.so
Config string = IF=vcan0;DBG_PRINT=1;
Version = 2
Interface version = 65536 (ecmc = 65536)
max plc funcs = 64
max plc func args = 10
max plc consts = 64
Construct func = @0x7fb36b81d310
Enter realtime func = @0x7fb36b81d220
Exit realtime func = @0x7fb36b81d230
Realtime func = @0x7fb36b81d270
Destruct func = @0x7fb36b81d240
dlhandle = @0xa45a50
Plc functions:
funcs[00]:
Name = "can_connect();"
Desc = double can_connect() : Connect to can interface (from config str).
Arg count = 0
func = @0x7fb36b81d280
funcs[01]:
Name = "can_connected();"
Desc = double can_connected() : Connected to can interface.
Arg count = 0
func = @0x7fb36b81d2a0
funcs[02]:
Name = "can_add_write(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);"
Desc = double can_add_write(canId,len,data0..data7) : Add write frame to can interface output buffer.
Arg count = 10
func = @0x7fb36b81d2e0
funcs[03]:
Name = "can_last_writes_error();"
Desc = double can_last_writes_error() : get error from last writes.
Arg count = 0
func = @0x7fb36b81d2c0
Plc constants:
```
# ecmcByteToArray Asub record
This module implements an asub record that can merge bytes into an array (waveform)
Usage:
1. Link bytes to inputs A..S.
2. Link array to VALA.
3. Set size of output in NOVA. This also defines how many inputs will be used.
Note: Max 18 bytes (input A..S) will be merged into the array.
EPICS database example of 7 bytes merged intio an array:
```
record(aSub, "$(P)CAN${CH_ID}-BasicConfigPackArray_") {
field(INAM, "ecmcByteToArrayInit")
field(SNAM, "ecmcByteToArray")
field(FTA, "UCHAR")
field(NOA, "1")
field(INPA, "$(P)CAN${CH_ID}-BasicConfigB0_.VAL") # Byte 0
field(FTB, "UCHAR")
field(NOB, "1")
field(INPB, "$(P)CAN${CH_ID}-VrefPwrCmdCalcB1_.VAL") # Byte 1
field(FTC, "UCHAR")
field(NOC, "1")
field(INPC, "$(P)CAN${CH_ID}-VrefPwrCmdCalcB2_.VAL") # Byte 2
field(FTD, "UCHAR")
field(NOD, "1")
field(INPD, "$(P)CAN${CH_ID}-VdcCtrlCmdCalcB3_.VAL") # Byte 3
field(FTE, "UCHAR")
field(NOE, "1")
field(INPE, "$(P)CAN${CH_ID}-VdcCtrlCmdCalcB4_.VAL") # Byte 4
field(FTF, "UCHAR")
field(NOF, "1")
field(INPF, "0") # Byte 5
field(FTG, "UCHAR")
field(NOG, "1")
field(INPG, "0") # Byte 6
field(FTVA, "UCHAR")
field(OUTA, "$(P)CAN${CH_ID}-SDO02-BasicConfig")
field(NOVA, "7") # 7 bytes (0..6 corresponds to input A..G)
field(FLNK, "$(P)CAN${CH_ID}-SDO02-BasicConfig.PROC") # Send the data
}
```
# Testing
You can use a virtual can, vcan network for testing:
## Prerequirements
Install can utils:
```
$ git clone https://github.com/linux-can/can-utils
$ cd can-utils
$ make
$ make install
```
## vcan setup
Start virt can 0 (vcan0) and candump:
```
$ sudo modprobe vcan
$ sudo ip link add dev vcan0 type vcan
$ sudo ip link set up vcan0
$ candump vcan0
```
candump will start to printout the raw can meassages on vcan0 network.
# Other intressting things
In future it could be an option to use one of these repos for the CANOpen support:
1. https://github.com/CANopenNode/CANopenNode
2. https://github.com/CANopenNode/CANopenSocket
3. https://github.com/marel-keytech/openCANopen

View File

@@ -8,8 +8,8 @@ E3_MODULE_VERSION:=master
# DEPENDENT MODULE VERSION
# For Example,
ECMC_DEP_VERSION:=6.3.0
ASYN_DEP_VERSION:=4.37.0
ECMC_DEP_VERSION:=6.3.2
ASYN_DEP_VERSION:=4.41.0
#DEVLIB2_DEP_VERSION:=2.9.0
#PCRE_DEP_VERSION:=8.41.0

View File

@@ -2,7 +2,7 @@
EPICS_BASE:=${HOME}/epics/base-7.0.4
E3_REQUIRE_NAME:=require
E3_REQUIRE_VERSION:=3.3.0
E3_REQUIRE_VERSION:=3.4.0
# The definitions shown below can also be placed in an untracked RELEASE.local
-include $(TOP)/../../RELEASE.local

View File

@@ -0,0 +1,21 @@
record(ai,"$(P)CAN-Stat-ComErr"){
field(DESC, "Communication error")
field(PINI, "$(PINI=1)")
field(VAL, "0")
field(DTYP, "asynInt32")
field(INP, "@asyn($(PORT),$(ADDR=0),$(TIMEOUT=1))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=asynInt32/plugin.can.com.error?")
field(SCAN, "I/O Intr")
}
record(bi,"$(P)CAN-Stat-Connected"){
field(DESC, "Connected")
field(PINI, "$(PINI=1)")
field(VAL, "0")
field(DTYP, "asynInt32")
field(INP, "@asyn($(PORT),$(ADDR=0),$(TIMEOUT=1))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=asynInt32/plugin.can.com.connected?")
field(SCAN, "I/O Intr")
field(ZNAM, "Not connected")
field(ONAM, "Connected")
field(ZSV, "MAJOR")
field(OSV, "NO_ALARM")
}

View File

@@ -0,0 +1,53 @@
# Device NMT state
#enum ecmc_nmt_state_act {
# NMT_NOT_VALID = 0,
# NMT_BOOT_UP = 1,
# NMT_STOPPED = 2,
# NMT_OP = 3,
# NMT_PREOP = 4
#};
# plugin.can.dev0.nmtstate
record(mbbi,"$(P)CAN${CH_ID}-Stat-NMT"){
field(DESC, "Device NMT state")
field(PINI, "$(PINI=1)")
field(VAL, "0")
field(DTYP, "asynUInt32Digital")
field(INP, "@asynMask($(PORT),$(ADDR=0),$(MASK=0xFFFFFFFF),$(TIMEOUT=1))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=asynUInt32Digital/plugin.can.dev${DEV_ID}.nmtstate?")
field(SCAN, "I/O Intr")
field(TSE, "$(TSE=-2)")
field(ZRST,"NMT_NOT_VALID")
field(ONST,"NMT_BOOT_UP")
field(TWST,"NMT_STOPPED")
field(THST,"NMT_OP")
field(FRST,"NMT_PREOP")
field(FVST,"NMT_NOT_VALID")
field(SXST,"NMT_NOT_VALID")
field(SVST,"NMT_NOT_VALID")
field(EIST,"NMT_NOT_VALID")
field(NIST,"NMT_NOT_VALID")
field(TEST,"NMT_NOT_VALID")
field(ELST,"NMT_NOT_VALID")
field(TVST,"NMT_NOT_VALID")
field(TTST,"NMT_NOT_VALID")
field(FTST,"NMT_NOT_VALID")
field(FFST,"NMT_NOT_VALID")
field(ZRVL,"0x0")
field(ONVL,"0x1")
field(TWVL,"0x2")
field(THVL,"0x3")
field(FRVL,"0x4")
field(FVVL,"0x5")
field(SXVL,"0x6")
field(SVVL,"0x7")
field(EIVL,"0x8")
field(NIVL,"0x9")
field(TEVL,"0xA")
field(ELVL,"0xB")
field(TVVL,"0xC")
field(TTVL,"0xD")
field(FTVL,"0xE")
field(FFVL,"0xF")
}

View File

@@ -0,0 +1,8 @@
record(ai,"$(P)CAN${CH_ID}-${suffix=}"){
field(DESC, "SDO error code")
field(PINI, "$(PINI=1)")
field(VAL, "0")
field(DTYP, "asynInt32")
field(INP, "@asyn($(PORT),$(ADDR=0),$(TIMEOUT=1))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=asynInt32/plugin.can.dev${DEV_ID}.pdo.${source=value}.error?")
field(SCAN, "I/O Intr")
}

View File

@@ -0,0 +1,12 @@
# Data source: plugin.can.dev3.pdo.testvalue
record(waveform,"$(P)CAN${CH_ID}-${suffix=}"){
info(asyn:READBACK,"1")
field(DESC, "PDO Data")
field(PINI, "1")
field(DTYP, "${DTYP=asynInt8ArrayIn}")
field(INP, "@asyn(${PORT},$(ADDR=0),$(TIMEOUT=1000))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=${DTYP=asynInt8ArrayIn}/plugin.can.dev${DEV_ID}.pdo.${source=value}?")
field(FTVL, "${FTVL=CHAR}")
field(NELM, "${NELM=1024}")
field(SCAN, "I/O Intr")
field(TSE, "0")
}

View File

@@ -0,0 +1,12 @@
# Data source: plugin.can.dev3.pdo.testvalue
record(waveform,"$(P)CAN${CH_ID}-${suffix=}"){
info(asyn:READBACK,"1")
field(DESC, "PDO Data")
# field(PINI, "1")
field(DTYP, "${DTYP=asynInt8ArrayOut}")
field(INP, "@asyn(${PORT},$(ADDR=0),$(TIMEOUT=1000))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=${DTYP=asynInt8ArrayOut}/plugin.can.dev${DEV_ID}.pdo.${source=value}=")
field(FTVL, "${FTVL=CHAR}")
field(NELM, "${NELM=1024}")
field(SCAN, "Passive")
field(TSE, "0")
}

View File

@@ -0,0 +1,8 @@
record(ai,"$(P)CAN${CH_ID}-${suffix=}"){
field(DESC, "SDO error code")
field(PINI, "$(PINI=1)")
field(VAL, "0")
field(DTYP, "asynInt32")
field(INP, "@asyn($(PORT),$(ADDR=0),$(TIMEOUT=1))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=asynInt32/plugin.can.dev${DEV_ID}.sdo.${source=value}.error?")
field(SCAN, "I/O Intr")
}

View File

@@ -0,0 +1,12 @@
# Data source: plugin.can.dev3.sdo.analogValues4
record(waveform,"$(P)CAN${CH_ID}-${suffix=}"){
info(asyn:READBACK,"1")
field(DESC, "SDO Data")
field(PINI, "1")
field(DTYP, "${DTYP=asynInt8ArrayIn}")
field(INP, "@asyn(${PORT},$(ADDR=0),$(TIMEOUT=1000))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=${DTYP=asynInt8ArrayIn}/plugin.can.dev${DEV_ID}.sdo.${source=value}?")
field(FTVL, "${FTVL=CHAR}")
field(NELM, "${NELM=1024}")
field(SCAN, "I/O Intr")
field(TSE, "0")
}

View File

@@ -0,0 +1,11 @@
# Data source: plugin.can.dev3.sdo.analogValues4
record(waveform,"$(P)CAN${CH_ID}-${suffix=}"){
info(asyn:READBACK,"1")
field(DESC, "SDO Data")
field(DTYP, "${DTYP=asynInt8ArrayOut}")
field(INP, "@asyn(${PORT},$(ADDR=0),$(TIMEOUT=1000))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=${DTYP=asynInt8ArrayOut}/plugin.can.dev${DEV_ID}.sdo.${source=value}=")
field(FTVL, "${FTVL=CHAR}")
field(NELM, "${NELM=1024}")
field(SCAN, "Passive")
field(TSE, "0")
}

13
db/test.template Normal file
View File

@@ -0,0 +1,13 @@
record(acalcout,"$(P)LeftMoveData_"){
field(DESC, "Move data from EL3702 to EL4732")
field(INAA, "$(P)ec$(MASTER_ID)-s$(SLAVE_AI)-EL3702_s$(NELM)-AI1-Array.VAL")
field(INPA, "$(P)LeftGain.VAL")
field(INPB, "$(P)LeftOffset.VAL")
field(INPC, "$(P)Volume.VAL")
field(CALC, "(C/200*A/100*AA+(B*3276.8))")
field(OUT, "$(P)ec$(MASTER_ID)-s$(SLAVE_AO)-EL4732_s$(NELM)-AO1-Array.VAL")
field(FLNK, "$(P)ec$(MASTER_ID)-s$(SLAVE_AO)-EL4732_s$(NELM)-AO1-Array.PROC")
field(NELM, "$(NELM)")
}

2
dbd/ecmc_socketcan.dbd Normal file
View File

@@ -0,0 +1,2 @@
function(ecmcWriteArrayUint8ElementsInit)
function(ecmcWriteArrayUint8Elements)

View File

@@ -304,4 +304,542 @@ r 0x583 [8] 0x60 0x90 0x26 0x01 0x00 0x00 0x00 0x00 # Ok from slave
w 0x603 [8] 0x60 0x08 0xF4 0xA9 0xBE 0xC0 0xCB 0xE1 # Send data
r 0x583 [8] 0x01 0x18 0x00 0x24 0x00 0x0E 0x00 0x0A # ???
# Check some pvs
camonitor IOC_TEST:CAN03-PWR_A IOC_TEST:CAN03-PWR_B IOC_TEST:CAN03-PWR_OUT IOC_TEST:CAN03-REFL_OUT IOC_TEST:CAN03-SDO01-Array IOC_TEST:CAN03-PDO01-Array IOC_TEST:CAN03-PWR_A
camonitor IOC_TEST:CAN03-PWR_A IOC_TEST:CAN03-PWR_B IOC_TEST:CAN03-PWR_OUT IOC_TEST:CAN03-REFL_OUT IOC_TEST:CAN03-V_REG IOC_TEST:CAN03-V_TEMP IOC_TEST:CAN03-I_DRV IOC_TEST:CAN03-I_PRE IOC_TEST:CAN03-I_1A IOC_TEST:CAN03-I_2A IOC_TEST:CAN03-V_REFL_SAVE IOC_TEST:CAN03-V_PLUSMON IOC_TEST:CAN03-V_I_DC IOC_TEST:CAN03-I_1B IOC_TEST:CAN03-I_2B IOC_TEST:CAN03-V_12V_MON IOC_TEST:CAN03-VREF_PWR_OPV IOC_TEST:CAN03-V_AUX_IN IOC_TEST:CAN03-V_5V_ACB IOC_TEST:CAN03-V_3V5 IOC_TEST:CAN03-AIR_INLET IOC_TEST:CAN03-AIR_OUTLET IOC_TEST:CAN03-SDO01-Array IOC_TEST:CAN03-PDO01-Array
# Most recent data dump
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x703 [1] 0x05
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x703 [1] 0x05
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x703 [1] 0x05
r 0x280 [0]
w 0x7E5 [0]STATE = READ_REQ_TRANSFER analogValues
STATE = READ_WAIT_FOR_CONF analogValues
w 0x080 [0]
w 0x700 [1] 0x05
w 0x603 [8] 0x40 0x40 0x26 0x00 0x00 0x00 0x00 0x00
STATE = READ_WAIT_FOR_DATA analogValues
r 0x583 [8] 0x41 0x40 0x26 0x00 0x38 0x00 0x00 0x00
w 0x603 [8] 0x61 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 7
r 0x583 [8] 0x00 0x18 0x00 0x24 0x00 0x0E 0x00 0x0A
w 0x603 [8] 0x71 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 14
r 0x583 [8] 0x10 0x00 0x00 0x00 0xC5 0x00 0x00 0x00
w 0x603 [8] 0x61 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 21
r 0x583 [8] 0x00 0xC2 0x01 0x00 0x00 0x00 0x00 0x35
w 0x603 [8] 0x71 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 28
r 0x583 [8] 0x10 0x1C 0x84 0x02 0x46 0x1A 0x3C 0x49
w 0x603 [8] 0x61 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 35
r 0x583 [8] 0x00 0xC2 0x01 0x00 0x00 0x00 0x00 0x00
w 0x603 [8] 0x71 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 42
r 0x583 [8] 0x10 0x00 0xC8 0x48 0x51 0x2F 0x00 0x00
w 0x603 [8] 0x61 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 49
r 0x583 [8] 0x00 0x5C 0x2D 0x7F 0x14 0x67 0x0D 0xAA
w 0x603 [8] 0x71 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 56
STATE = READ_IDLE analogValues
All data read from slave SDO.
data[00]: 24
data[01]: 36
data[02]: 14
data[03]: 10
data[04]: 0
data[05]: 197
data[06]: 0
data[07]: 450
data[08]: 0
data[09]: 0
data[10]: 7221
data[11]: 644
data[12]: 6726
data[13]: 18748
data[14]: 450
data[15]: 0
data[16]: 0
data[17]: 0
data[18]: 18632
data[19]: 12113
data[20]: 0
data[21]: 11612
data[22]: 5247
data[23]: 3431
data[24]: 170
data[25]: 193
data[26]: 0
data[27]: 5000
r 0x583 [8] 0x11 0x00 0xC1 0x00 0x00 0x00 0x88 0x13
r 0x280 [0]
r 0x683 [4] 0x00 0x00 0x00 0x00
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
data[00]: 0
data[01]: 0
data[02]: 16395
data[03]: 8196
r 0x183 [8] 0x00 0x00 0x00 0x00 0x0B 0x40 0x04 0x20
r 0x703 [1] 0x05
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x703 [1] 0x05
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x703 [1] 0x05
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
STATE = READ_REQ_TRANSFER analogValues
STATE = READ_WAIT_FOR_CONF analogValues
w 0x603 [8] 0x40 0x40 0x26 0x00 0x00 0x00 0x00 0x00
STATE = READ_WAIT_FOR_DATA analogValues
r 0x583 [8] 0x41 0x40 0x26 0x00 0x38 0x00 0x00 0x00
w 0x603 [8] 0x61 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 7
r 0x583 [8] 0x00 0x18 0x00 0x24 0x00 0x0E 0x00 0x0A
w 0x603 [8] 0x71 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 14
r 0x583 [8] 0x10 0x00 0x00 0x00 0xC5 0x00 0x00 0x00
w 0x603 [8] 0x61 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 21
r 0x583 [8] 0x00 0xC2 0x01 0x00 0x00 0x00 0x00 0x35
w 0x603 [8] 0x71 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 28
r 0x583 [8] 0x10 0x1C 0x84 0x02 0x46 0x1A 0x3C 0x49
w 0x603 [8] 0x61 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 35
r 0x583 [8] 0x00 0xC2 0x01 0x00 0x00 0x00 0x00 0x00
w 0x603 [8] 0x71 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 42
r 0x583 [8] 0x10 0x00 0xC8 0x48 0x51 0x2F 0x00 0x00
w 0x603 [8] 0x61 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 49
r 0x583 [8] 0x00 0x5C 0x2D 0x7F 0x14 0x67 0x0D 0xAA
w 0x603 [8] 0x71 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 56
STATE = READ_IDLE analogValues
All data read from slave SDO.
data[00]: 24
data[01]: 36
data[02]: 14
data[03]: 10
data[04]: 0
data[05]: 197
data[06]: 0
data[07]: 450
data[08]: 0
data[09]: 0
data[10]: 7221
data[11]: 644
data[12]: 6726
data[13]: 18748
data[14]: 450
data[15]: 0
data[16]: 0
data[17]: 0
data[18]: 18632
data[19]: 12113
data[20]: 0
data[21]: 11612
data[22]: 5247
data[23]: 3431
data[24]: 170
data[25]: 193
data[26]: 0
data[27]: 5000
r 0x583 [8] 0x11 0x00 0xC1 0x00 0x00 0x00 0x88 0x13
r 0x280 [0]
r 0x280 [0]
r 0x703 [1] 0x05
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x280 [0]
r 0x703 [1] 0x05
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x683 [4] 0x00 0x00 0x00 0x00
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
data[00]: 0
data[01]: 0
data[02]: 16395
data[03]: 8196
r 0x183 [8] 0x00 0x00 0x00 0x00 0x0B 0x40 0x04 0x20
r 0x703 [1] 0x05
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x703 [1] 0x05
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
STATE = READ_REQ_TRANSFER analogValues
STATE = READ_WAIT_FOR_CONF analogValues
w 0x603 [8] 0x40 0x40 0x26 0x00 0x00 0x00 0x00 0x00
STATE = READ_WAIT_FOR_DATA analogValues
r 0x583 [8] 0x41 0x40 0x26 0x00 0x38 0x00 0x00 0x00
w 0x603 [8] 0x61 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 7
r 0x583 [8] 0x00 0x18 0x00 0x24 0x00 0x0E 0x00 0x0A
w 0x603 [8] 0x71 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 14
r 0x583 [8] 0x10 0x00 0x00 0x00 0xC5 0x00 0x00 0x00
w 0x603 [8] 0x61 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 21
r 0x583 [8] 0x00 0xC2 0x01 0x00 0x00 0x00 0x00 0x35
w 0x603 [8] 0x71 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 28
r 0x583 [8] 0x10 0x1C 0x84 0x02 0x46 0x1A 0x3C 0x49
w 0x603 [8] 0x61 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 35
r 0x583 [8] 0x00 0xC2 0x01 0x00 0x00 0x00 0x00 0x00
w 0x603 [8] 0x71 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 42
r 0x583 [8] 0x10 0x00 0xC8 0x48 0x51 0x2F 0x00 0x00
w 0x603 [8] 0x61 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 49
r 0x583 [8] 0x00 0x5C 0x2D 0x7F 0x14 0x67 0x0D 0xAA
w 0x603 [8] 0x71 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 56
STATE = READ_IDLE analogValues
All data read from slave SDO.
data[00]: 24
data[01]: 36
data[02]: 14
data[03]: 10
data[04]: 0
data[05]: 197
data[06]: 0
data[07]: 450
data[08]: 0
data[09]: 0
data[10]: 7221
data[11]: 644
data[12]: 6726
data[13]: 18748
data[14]: 450
data[15]: 0
data[16]: 0
data[17]: 0
data[18]: 18632
data[19]: 12113
data[20]: 0
data[21]: 11612
data[22]: 5247
data[23]: 3431
data[24]: 170
data[25]: 193
data[26]: 0
data[27]: 5000
r 0x583 [8] 0x11 0x00 0xC1 0x00 0x00 0x00 0x88 0x13
r 0x280 [0]
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x280 [0]
r 0x703 [1] 0x05
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x280 [0]
r 0x703 [1] 0x05
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x280 [0]
r 0x703 [1] 0x05
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x683 [4] 0x00 0x00 0x00 0x00
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
STATE = READ_REQ_TRANSFER analogValues
STATE = READ_WAIT_FOR_CONF analogValues
w 0x603 [8] 0x40 0x40 0x26 0x00 0x00 0x00 0x00 0x00
STATE = READ_WAIT_FOR_DATA analogValues
r 0x583 [8] 0x41 0x40 0x26 0x00 0x38 0x00 0x00 0x00
w 0x603 [8] 0x61 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 7
r 0x583 [8] 0x00 0x18 0x00 0x24 0x00 0x0E 0x00 0x0A
w 0x603 [8] 0x71 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 14
r 0x583 [8] 0x10 0x00 0x00 0x00 0xC5 0x00 0x00 0x00
w 0x603 [8] 0x61 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 21
r 0x583 [8] 0x00 0xC2 0x01 0x00 0x00 0x00 0x00 0x35
w 0x603 [8] 0x71 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 28
r 0x583 [8] 0x10 0x1C 0x84 0x02 0x46 0x1A 0x3C 0x49
w 0x603 [8] 0x61 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 35
r 0x583 [8] 0x00 0xC2 0x01 0x00 0x00 0x00 0x00 0x00
w 0x603 [8] 0x71 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 42
r 0x583 [8] 0x10 0x00 0xC8 0x48 0x51 0x2F 0x00 0x00
w 0x603 [8] 0x61 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 49
r 0x583 [8] 0x00 0x5C 0x2D 0x7F 0x14 0x67 0x0D 0xAA
w 0x603 [8] 0x71 0x40 0x26 0x00 0x00 0x00 0x00 0x00
recived bytes = 56
STATE = READ_IDLE analogValues
All data read from slave SDO.
data[00]: 24
data[01]: 36
data[02]: 14
data[03]: 10
data[04]: 0
data[05]: 197
data[06]: 0
data[07]: 450
data[08]: 0
data[09]: 0
data[10]: 7221
data[11]: 644
data[12]: 6726
data[13]: 18748
data[14]: 450
data[15]: 0
data[16]: 0
data[17]: 0
data[18]: 18632
data[19]: 12113
data[20]: 0
data[21]: 11612
data[22]: 5247
data[23]: 3431
data[24]: 170
data[25]: 193
data[26]: 0
data[27]: 5000
r 0x583 [8] 0x11 0x00 0xC1 0x00 0x00 0x00 0x88 0x13
r 0x280 [0]
data[00]: 0
data[01]: 0
data[02]: 16395
data[03]: 8196
r 0x183 [8] 0x00 0x00 0x00 0x00 0x0B 0x40 0x04 0x20
r 0x280 [0]
r 0x703 [1] 0x05
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x280 [0]
r 0x703 [1] 0x05
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x280 [0]
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x280 [0]
r 0x280 [0]
r 0x703 [1] 0x05
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
# Basic configuration write (maybe should write a 4800 (=48%) to byte 3 and 4)
caput -a IOC_TEST:CAN03-SDO02-BasicConfig 7 0 0 0 0 0 0
# or better 48000 VDC_CTRL (48%), fan stops of pmu and amplifier on is LED is not on anymore
caput -a IOC_TEST:CAN03-SDO02-BasicConfig 7 0 0 0 192 18 0 0
This will happen:
WRITEVALUE basicConfig
STATE = WRITE_REQ_TRANSFER basicConfig
STATE = WRITE_WAIT_FOR_CONF basicConfig
w 0x603 [8] 0x21 0x90 0x26 0x01 0x07 0x00 0x00 0x00
writeDataStateMachine basicConfig
STATE = WRITE_DATA basicConfig
r 0x583 [8] 0x60 0x90 0x26 0x01 0x00 0x00 0x00 0x00
w 0x603 [8] 0x01 0x00 0x00 0x00 0xC0 0x12 0x00 0x00
writeDataStateMachine basicConfig
STATE = WRITE_IDLE basicConfig
All data written to slave SDO.
# Amplifier on is bit 0 of byte 0, so to power on just execute:
caput -a IOC_TEST:CAN03-SDO02-BasicConfig 7 1 0 0 192 18 0 0
# After basic configuration the r 280 will stop () this means the pmu now have basic configuration
r 0x583 [8] 0x11 0x00 0xC5 0x00 0x00 0x00 0x88 0x13
r 0x703 [1] 0x05
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
r 0x703 [1] 0x05
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
w 0x7E5 [0]
w 0x080 [0]
w 0x700 [1] 0x05
# Some commands
IOC_TEST:CAN03-PowerOnCmd
IOC_TEST:CAN03-VrefPwrCmd
IOC_TEST:CAN03-VdcCtrlCmd
## Turn amplifier on
caput IOC_TEST:CAN03-PowerOnCmd 1
WRITEVALUE basicConfig
STATE = WRITE_REQ_TRANSFER basicConfig
STATE = WRITE_WAIT_FOR_CONF basicConfig
w 0x603 [8] 0x21 0x90 0x26 0x01 0x07 0x00 0x00 0x00
writeDataStateMachine basicConfig
STATE = WRITE_DATA basicConfig
r 0x583 [8] 0x60 0x90 0x26 0x01 0x00 0x00 0x00 0x00
w 0x603 [8] 0x01 0x01 0x00 0x00 0x00 0x00 0x00 0x00
writeDataStateMachine basicConfig
STATE = WRITE_IDLE basicConfig
All data written to slave SDO.
data[00]: 1
data[01]: 0
data[02]: 0
data[03]: 0
## Set VrefOwr to 100
caput IOC_TEST:CAN03-VrefPwrCmd 100
NOT WORKING.. Will get the below???
r 0x583 [8] 0x11 0x00 0xC5 0x00 0x00 0x00 0x88 0x13
WRITEVALUE basicConfig
STATE = WRITE_REQ_TRANSFER basicConfig
STATE = WRITE_WAIT_FOR_CONF basicConfig
w 0x603 [8] 0x21 0x90 0x26 0x01 0x07 0x00 0x00 0x00
writeDataStateMachine basicConfig
STATE = WRITE_DATA basicConfig
r 0x583 [8] 0x60 0x90 0x26 0x01 0x00 0x00 0x00 0x00
w 0x603 [8] 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00
writeDataStateMachine basicConfig
STATE = WRITE_IDLE basicConfig
All data written to slave SDO.
## Set VdcCtrlCmd to 5000
caput IOC_TEST:CAN03-VdcCtrlCmd 5000
NOT WORKING.. Will get the below???
WRITEVALUE basicConfig
STATE = WRITE_REQ_TRANSFER basicConfig
STATE = WRITE_WAIT_FOR_CONF basicConfig
w 0x603 [8] 0x21 0x90 0x26 0x01 0x07 0x00 0x00 0x00
writeDataStateMachine basicConfig
STATE = WRITE_DATA basicConfig
r 0x583 [8] 0x60 0x90 0x26 0x01 0x00 0x00 0x00 0x00
w 0x603 [8] 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00
writeDataStateMachine basicConfig
STATE = WRITE_IDLE basicConfig
All data written to slave SDO.

View File

@@ -1,65 +0,0 @@
#
# Copyright (c) 2019 European Spallation Source ERIC
#
# The program is free software: you can redistribute
# it and/or modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation, either version 2 of the
# License, or any newer version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see https://www.gnu.org/licenses/gpl-2.0.txt
#
#
# Author : anderssandstrom
# email : anderssandstrom@esss.se
# Date : 2020Mar22-1607-33CET
# version : 0.0.0
#
# template file is generated by ./e3TemplateGenerator.bash with bf03d40
# Please look at many other _module_.Makefile in e3-* repository
#
## The following lines are mandatory, please don't change them.
where_am_I := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include $(E3_REQUIRE_TOOLS)/driver.makefile
include $(E3_REQUIRE_CONFIG)/DECOUPLE_FLAGS
ifneq ($(strip $(ASYN_DEP_VERSION)),)
asyn_VERSION=$(ASYN_DEP_VERSION)
endif
ifneq ($(strip $(ECMC_DEP_VERSION)),)
ecmc_VERSION=$(ECMC_DEP_VERSION)
endif
APP:=ecmc_plugin_socketcanApp
APPDB:=$(APP)/Db
APPSRC:=$(APP)/src
USR_CFLAGS += -shared -fPIC -Wall -Wextra
USR_LDFLAGS += -lstdc++
USR_INCLUDES += -I$(where_am_I)$(APPSRC)
TEMPLATES += $(wildcard $(APPDB)/*.db)
TEMPLATES += $(wildcard $(APPDB)/*.template)
SOURCES += $(APPSRC)/ecmcPluginSocketCAN.c
SOURCES += $(APPSRC)/ecmcSocketCAN.cpp
SOURCES += $(APPSRC)/ecmcSocketCANWrap.cpp
SOURCES += $(APPSRC)/ecmcSocketCANWriteBuffer.cpp
SOURCES += $(APPSRC)/ecmcCANOpenSDO.cpp
SOURCES += $(APPSRC)/ecmcCANOpenPDO.cpp
db:
.PHONY: db
vlibs:
.PHONY: vlibs
###

View File

@@ -1,65 +0,0 @@
#
# Copyright (c) 2019 European Spallation Source ERIC
#
# The program is free software: you can redistribute
# it and/or modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation, either version 2 of the
# License, or any newer version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see https://www.gnu.org/licenses/gpl-2.0.txt
#
#
# Author : anderssandstrom
# email : anderssandstrom@esss.se
# Date : 2020Mar22-1607-33CET
# version : 0.0.0
#
# template file is generated by ./e3TemplateGenerator.bash with bf03d40
# Please look at many other _module_.Makefile in e3-* repository
#
## The following lines are mandatory, please don't change them.
where_am_I := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include $(E3_REQUIRE_TOOLS)/driver.makefile
include $(E3_REQUIRE_CONFIG)/DECOUPLE_FLAGS
ifneq ($(strip $(ASYN_DEP_VERSION)),)
asyn_VERSION=$(ASYN_DEP_VERSION)
endif
ifneq ($(strip $(ECMC_DEP_VERSION)),)
ecmc_VERSION=$(ECMC_DEP_VERSION)
endif
APP:=ecmc_plugin_socketcanApp
APPDB:=$(APP)/Db
APPSRC:=$(APP)/src
USR_CFLAGS += -shared -fPIC -Wall -Wextra
USR_LDFLAGS += -lstdc++
USR_INCLUDES += -I$(where_am_I)$(APPSRC)
TEMPLATES += $(wildcard $(APPDB)/*.db)
TEMPLATES += $(wildcard $(APPDB)/*.template)
SOURCES += $(APPSRC)/ecmcPluginSocketCAN.c
SOURCES += $(APPSRC)/ecmcSocketCAN.cpp
SOURCES += $(APPSRC)/ecmcSocketCANWrap.cpp
SOURCES += $(APPSRC)/ecmcSocketCANWriteBuffer.cpp
SOURCES += $(APPSRC)/ecmcCANOpenSDO.cpp
SOURCES += $(APPSRC)/ecmcCANOpenPDO.cpp
db:
.PHONY: db
vlibs:
.PHONY: vlibs
###

View File

@@ -1,140 +0,0 @@
/*************************************************************************\
* Copyright (c) 2019 European Spallation Source ERIC
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcCANOpenPDO.cpp
*
* Created on: Mar 22, 2020
* Author: anderssandstrom
* Credits to https://github.com/sgreg/dynamic-loading
*
\*************************************************************************/
// Needed to get headers in ecmc right...
#define ECMC_IS_PLUGIN
#include <sstream>
#include "ecmcCANOpenPDO.h"
/**
* ecmc ecmcCANOpenPDO class
*/
ecmcCANOpenPDO::ecmcCANOpenPDO(ecmcSocketCANWriteBuffer* writeBuffer,
uint32_t cobId, // 0x580 + CobId
ecmc_can_direction rw,
uint32_t ODSize,
int readTimeoutMs,
int writeCycleMs,
int exeSampleTimeMs,
int dbgMode) {
writeBuffer_ = writeBuffer;
cobId_ = cobId;
ODSize_ = ODSize;
if(ODSize_ > 8) {
ODSize_ = 8;
}
readTimeoutMs_ = readTimeoutMs;
writeCycleMs_ = writeCycleMs;
exeSampleTimeMs_ = exeSampleTimeMs;
rw_ = rw;
exeCounter_ = 0;
busy_ = 0;
errorCode_ = 0;
dataBuffer_ = new uint8_t(ODSize_);
dbgMode_ = dbgMode;
writeFrame_.can_id = cobId_;
writeFrame_.can_dlc = ODSize; // data length
writeFrame_.data[0] = 0; // request read cmd
writeFrame_.data[1] = 0;
writeFrame_.data[2] = 0;
writeFrame_.data[3] = 0;
writeFrame_.data[4] = 0;
writeFrame_.data[5] = 0;
writeFrame_.data[6] = 0;
writeFrame_.data[7] = 0;
}
ecmcCANOpenPDO::~ecmcCANOpenPDO() {
delete[] dataBuffer_;
}
void ecmcCANOpenPDO::execute() {
exeCounter_++;
if(rw_ == DIR_READ) {
if(exeCounter_* exeSampleTimeMs_ >= readTimeoutMs_) {
errorCode_ = ECMC_CAN_ERROR_PDO_TIMEOUT;
if(dbgMode_) {
printf("ECMC_CAN_ERROR_PDO_TIMEOUT (0x%x)\n",errorCode_);
}
exeCounter_ = 0;
}
}
else { //DIR_WRITE
if(writeCycleMs_<=0) { // Only write on demand if cycle is less than 0
exeCounter_ = 0;
return;
}
if(exeCounter_* exeSampleTimeMs_ >= writeCycleMs_) {
writeValue(); // write in defined cycle
exeCounter_ = 0;
}
}
return;
}
// new rx frame recived!
void ecmcCANOpenPDO::newRxFrame(can_frame *frame) {
// Wait for:
if(rw_ == DIR_READ) {
if(validateFrame(frame)) {
memset(dataBuffer_,0,ODSize_);
memcpy(dataBuffer_, &(frame->data[0]),frame->can_dlc);
errorCode_ = 0;
if(dbgMode_) {
printBuffer();
}
}
}
}
void ecmcCANOpenPDO::printBuffer() {
if(!dataBuffer_) {
return;
}
for(uint32_t i = 0; i < ODSize_; i = i + 2) {
uint16_t test;
memcpy(&test,&dataBuffer_[i],2);
printf("data[%02d]: %u\n",i/2,test);
}
}
// r 0x183 [8] 0x00 0x00 0x00 0x00 0x0B 0x40 0x04 0x20
int ecmcCANOpenPDO::validateFrame(can_frame *frame) {
if(frame->can_id != cobId_) {
return 0;
}
if(frame->can_dlc != ODSize_) {
return 0;
}
return 1;
}
void ecmcCANOpenPDO::setValue(uint64_t data) {
memcpy(dataBuffer_, &data, ODSize_);
}
int ecmcCANOpenPDO::writeValue() {
if(writeFrame_.can_dlc > 0) {
memcpy(&(writeFrame_.data[0]), dataBuffer_ ,writeFrame_.can_dlc);
}
return writeBuffer_->addWriteCAN(&writeFrame_);
}

View File

@@ -1,592 +0,0 @@
/*************************************************************************\
* Copyright (c) 2019 European Spallation Source ERIC
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcSocketCAN.cpp
*
* Created on: Mar 22, 2020
* Author: anderssandstrom
* Credits to https://github.com/sgreg/dynamic-loading
*
\*************************************************************************/
// Needed to get headers in ecmc right...
#define ECMC_IS_PLUGIN
#define ECMC_PLUGIN_ASYN_PREFIX "plugin.can"
#define ECMC_PLUGIN_ASYN_ENABLE "enable"
#include <sstream>
#include "ecmcSocketCAN.h"
#include "ecmcPluginClient.h"
#include "ecmcAsynPortDriver.h"
#include "ecmcAsynPortDriverUtils.h"
#include "epicsThread.h"
// Start worker for socket read()
void f_worker_read(void *obj) {
if(!obj) {
printf("%s/%s:%d: Error: Worker read thread ecmcSocketCAN object NULL..\n",
__FILE__, __FUNCTION__, __LINE__);
return;
}
ecmcSocketCAN * canObj = (ecmcSocketCAN*)obj;
canObj->doReadWorker();
}
// Start worker for socket connect()
void f_worker_connect(void *obj) {
if(!obj) {
printf("%s/%s:%d: Error: Worker connect thread ecmcSocketCAN object NULL..\n",
__FILE__, __FUNCTION__, __LINE__);
return;
}
ecmcSocketCAN * canObj = (ecmcSocketCAN*)obj;
canObj->doConnectWorker();
}
/** ecmc ecmcSocketCAN class
* This object can throw:
* - bad_alloc
* - invalid_argument
* - runtime_error
*/
ecmcSocketCAN::ecmcSocketCAN(char* configStr,
char* portName,
int exeSampleTimeMs)
: asynPortDriver(portName,
1, /* maxAddr */
asynInt32Mask | asynFloat64Mask | asynFloat32ArrayMask |
asynFloat64ArrayMask | asynEnumMask | asynDrvUserMask |
asynOctetMask | asynInt8ArrayMask | asynInt16ArrayMask |
asynInt32ArrayMask | asynUInt32DigitalMask, /* Interface mask */
asynInt32Mask | asynFloat64Mask | asynFloat32ArrayMask |
asynFloat64ArrayMask | asynEnumMask | asynDrvUserMask |
asynOctetMask | asynInt8ArrayMask | asynInt16ArrayMask |
asynInt32ArrayMask | asynUInt32DigitalMask, /* Interrupt mask */
ASYN_CANBLOCK , /*NOT ASYN_MULTI_DEVICE*/
1, /* Autoconnect */
0, /* Default priority */
0) /* Default stack size */ {
// Init
cfgCanIFStr_ = NULL;
cfgDbgMode_ = 0;
cfgAutoConnect_ = 1;
destructs_ = 0;
socketId_ = -1;
connected_ = 0;
writeBuffer_ = NULL;
testSdo_ = NULL;
testPdo_ = NULL;
lssPdo_ = NULL;
syncPdo_ = NULL;
heartPdo_ = NULL;
basicConfSdo_ = NULL;
cycleCounter_ = 0;
exeSampleTimeMs_ = exeSampleTimeMs;
memset(&ifr_,0,sizeof(struct ifreq));
memset(&rxmsg_,0,sizeof(struct can_frame));
memset(&addr_,0,sizeof(struct sockaddr_can));
parseConfigStr(configStr); // Assigns all configs
// Check valid nfft
if(!cfgCanIFStr_ ) {
throw std::out_of_range("CAN inteface must be defined (can0, vcan0...).");
}
// Create worker thread for reading socket
std::string threadname = "ecmc." ECMC_PLUGIN_ASYN_PREFIX".read";
if(epicsThreadCreate(threadname.c_str(), 0, 32768, f_worker_read, this) == NULL) {
throw std::runtime_error("Error: Failed create worker thread for read().");
}
// Create worker thread for connecting socket
threadname = "ecmc." ECMC_PLUGIN_ASYN_PREFIX".connect";
if(epicsThreadCreate(threadname.c_str(), 0, 32768, f_worker_connect, this) == NULL) {
throw std::runtime_error("Error: Failed create worker thread for connect().");
}
if(cfgAutoConnect_) {
connectPrivate();
}
writeBuffer_ = new ecmcSocketCANWriteBuffer(socketId_, cfgDbgMode_);
testSdo_ = new ecmcCANOpenSDO( writeBuffer_, 0x583,0x603,DIR_READ,0x2640,0,56,7000,exeSampleTimeMs_, cfgDbgMode_);
testPdo_ = new ecmcCANOpenPDO( writeBuffer_, 0x183,DIR_READ,8,10000,0,exeSampleTimeMs_, cfgDbgMode_);
// Test LSS heartbeat "master" signal. This makes the led on pmu905 to go to "Normal Communication"
// can0 0x7E5 [0]
lssPdo_ = new ecmcCANOpenPDO( writeBuffer_, 0x7E5,DIR_WRITE,0,0,1000,exeSampleTimeMs_, cfgDbgMode_);
// Test sync signal
// can0 0x80 [0]
syncPdo_ = new ecmcCANOpenPDO( writeBuffer_, 0x80,DIR_WRITE,0,0,1000,exeSampleTimeMs_, cfgDbgMode_);
// Test heartbeat signal
// can0 0x701 [1] 05
//can_add_write(1793,1,5,0,0,0,0,0,0,0);
heartPdo_ = new ecmcCANOpenPDO( writeBuffer_, 0x701,DIR_WRITE,1,0,1000,exeSampleTimeMs_, cfgDbgMode_);
heartPdo_->setValue(5);
basicConfSdo_ = new ecmcCANOpenSDO( writeBuffer_, 0x583,0x603,DIR_WRITE,0x2690,1,7,0,exeSampleTimeMs_, cfgDbgMode_);
//byte0 = 0
//byte1 = 0
//byte2 = 0
//byte 3,4 = 5000
//byte 5 =0
//byte 6 =0
//byte 7 =0
// => 0x1388000
uint64_t tempVal = 0x1388000;
uint8_t * val = (uint8_t*)&tempVal;
basicConfSdo_->setValue(val,7);
initAsyn();
}
ecmcSocketCAN::~ecmcSocketCAN() {
// kill worker
destructs_ = 1; // maybe need todo in other way..
doWriteEvent_.signal();
doConnectEvent_.signal();
}
void ecmcSocketCAN::parseConfigStr(char *configStr) {
// check config parameters
if (configStr && configStr[0]) {
char *pOptions = strdup(configStr);
char *pThisOption = pOptions;
char *pNextOption = pOptions;
while (pNextOption && pNextOption[0]) {
pNextOption = strchr(pNextOption, ';');
if (pNextOption) {
*pNextOption = '\0'; /* Terminate */
pNextOption++; /* Jump to (possible) next */
}
// ECMC_PLUGIN_DBG_PRINT_OPTION_CMD (1/0)
if (!strncmp(pThisOption, ECMC_PLUGIN_DBG_PRINT_OPTION_CMD, strlen(ECMC_PLUGIN_DBG_PRINT_OPTION_CMD))) {
pThisOption += strlen(ECMC_PLUGIN_DBG_PRINT_OPTION_CMD);
cfgDbgMode_ = atoi(pThisOption);
}
// ECMC_PLUGIN_CONNECT_OPTION_CMD (1/0)
if (!strncmp(pThisOption, ECMC_PLUGIN_CONNECT_OPTION_CMD, strlen(ECMC_PLUGIN_CONNECT_OPTION_CMD))) {
pThisOption += strlen(ECMC_PLUGIN_DBG_PRINT_OPTION_CMD);
cfgAutoConnect_ = atoi(pThisOption);
}
// ECMC_PLUGIN_IF_OPTION_CMD (Source string)
else if (!strncmp(pThisOption, ECMC_PLUGIN_IF_OPTION_CMD, strlen(ECMC_PLUGIN_IF_OPTION_CMD))) {
pThisOption += strlen(ECMC_PLUGIN_IF_OPTION_CMD);
cfgCanIFStr_=strdup(pThisOption);
}
pThisOption = pNextOption;
}
free(pOptions);
}
if(!cfgCanIFStr_) {
throw std::invalid_argument( "CAN interface not defined.");
}
}
// For connect commands over asyn or plc. let worker connect
void ecmcSocketCAN::connectExternal() {
if(!connected_) {
doConnectEvent_.signal(); // let worker start
}
}
void ecmcSocketCAN::connectPrivate() {
if((socketId_ = socket(PF_CAN, SOCK_RAW, CAN_RAW)) == -1) {
throw std::runtime_error( "Error while opening socket.");
return;
}
strcpy(ifr_.ifr_name, cfgCanIFStr_);
ioctl(socketId_, SIOCGIFINDEX, &ifr_);
addr_.can_family = AF_CAN;
addr_.can_ifindex = ifr_.ifr_ifindex;
printf("%s at index %d\n", cfgCanIFStr_, ifr_.ifr_ifindex);
if(bind(socketId_, (struct sockaddr *)&addr_, sizeof(addr_)) == -1) {
throw std::runtime_error( "Error in socket bind.");
return;
}
connected_ = 1;
}
int ecmcSocketCAN::getConnected() {
return connected_;
}
// Read socket worker
void ecmcSocketCAN::doReadWorker() {
while(true) {
if(destructs_) {
break;
}
// Wait for new CAN frame
// TODO MUST CHECK RETRUN VALUE OF READ!!!!!
read(socketId_, &rxmsg_, sizeof(rxmsg_));
if(testSdo_) {
testSdo_->newRxFrame(&rxmsg_);
}
if(testPdo_) {
testPdo_->newRxFrame(&rxmsg_);
}
if(lssPdo_) {
lssPdo_->newRxFrame(&rxmsg_);
}
if(syncPdo_) {
syncPdo_->newRxFrame(&rxmsg_);
}
if(heartPdo_) {
heartPdo_->newRxFrame(&rxmsg_);
}
if(basicConfSdo_) {
basicConfSdo_->newRxFrame(&rxmsg_);
}
if(cfgDbgMode_) {
// Simulate candump printout
printf("r 0x%03X", rxmsg_.can_id);
printf(" [%d]", rxmsg_.can_dlc);
for(int i=0; i<rxmsg_.can_dlc; i++ ) {
printf(" 0x%02X", rxmsg_.data[i]);
}
printf("\n");
}
}
}
// Connect socket worker
void ecmcSocketCAN::doConnectWorker() {
while(true) {
if(destructs_) {
return;
}
doConnectEvent_.wait();
if(destructs_) {
return;
}
connectPrivate();
}
}
int ecmcSocketCAN::getlastWritesError() {
if(!writeBuffer_) {
return ECMC_CAN_ERROR_WRITE_BUFFER_NULL;
}
return writeBuffer_->getlastWritesError();
}
int ecmcSocketCAN::addWriteCAN(uint32_t canId,
uint8_t len,
uint8_t data0,
uint8_t data1,
uint8_t data2,
uint8_t data3,
uint8_t data4,
uint8_t data5,
uint8_t data6,
uint8_t data7) {
if(!writeBuffer_) {
return ECMC_CAN_ERROR_WRITE_BUFFER_NULL;
}
writeBuffer_->addWriteCAN(canId,
len,
data0,
data1,
data2,
data3,
data4,
data5,
data6,
data7);
return 0;
}
void ecmcSocketCAN::execute() {
if(testSdo_) {
testSdo_->execute();
}
if(testPdo_) {
testPdo_->execute();
}
if(lssPdo_) {
lssPdo_->execute();
}
if(syncPdo_) {
syncPdo_->execute();
}
if(heartPdo_) {
heartPdo_->execute();
}
cycleCounter_++;
if(basicConfSdo_) {
basicConfSdo_->execute();
if(cycleCounter_ > 10000) {
cycleCounter_ = 0;
printf("################################### TEST WRITE SDO#############\n");
basicConfSdo_->writeValue();
}
}
return;
}
void ecmcSocketCAN::initAsyn() {
// Add enable "plugin.fft%d.enable"
/*std::string paramName =ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_ENABLE;
if( createParam(0, paramName.c_str(), asynParamInt32, &asynEnableId_) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter enable");
}
setIntegerParam(asynEnableId_, cfgEnable_);
// Add rawdata "plugin.fft%d.rawdata"
paramName =ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_RAWDATA;
if( createParam(0, paramName.c_str(), asynParamFloat64Array, &asynRawDataId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter rawdata");
}
doCallbacksFloat64Array(rawDataBuffer_, cfgNfft_, asynRawDataId_,0);
// Add rawdata "plugin.fft%d.preprocdata"
paramName =ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_PPDATA;
if( createParam(0, paramName.c_str(), asynParamFloat64Array, &asynPPDataId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter preprocdata");
}
doCallbacksFloat64Array(prepProcDataBuffer_, cfgNfft_, asynPPDataId_,0);
n
// Add fft amplitude "plugin.fft%d.fftamplitude"
paramName = ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_FFT_AMP;
if( createParam(0, paramName.c_str(), asynParamFloat64Array, &asynFFTAmpId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter fftamplitude");
}
doCallbacksFloat64Array(fftBufferResultAmp_, cfgNfft_/2+1, asynFFTAmpId_,0);
// Add fft "plugin.fft%d.mode"
paramName = ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_FFT_MODE;
if( createParam(0, paramName.c_str(), asynParamInt32, &asynFFTModeId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter mode");
}
setIntegerParam(asynFFTModeId_, (epicsInt32)cfgMode_);
// Add fft "plugin.fft%d.status"
paramName = ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_FFT_STAT;
if( createParam(0, paramName.c_str(), asynParamInt32, &asynFFTStatId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter status");
}
setIntegerParam(asynFFTStatId_, (epicsInt32)status_);
// Add fft "plugin.fft%d.source"
paramName = ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_FFT_SOURCE;
if( createParam(0, paramName.c_str(), asynParamInt8Array, &asynSourceId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter source");
}
doCallbacksInt8Array(cfgCanIFStr_, strlen(cfgCanIFStr_), asynSourceId_,0);
// Add fft "plugin.fft%d.trigg"
paramName = ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_FFT_TRIGG;
if( createParam(0, paramName.c_str(), asynParamInt32, &asynTriggId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter trigg");
}
setIntegerParam(asynTriggId_, (epicsInt32)triggOnce_);
// Add fft "plugin.fft%d.fftxaxis"
paramName = ECMC_PLUGIN_nSYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_FFT_X_FREQS;
if( createParam(0, paramName.c_str(), asynParamFloat64Array, &asynFFTXAxisId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter xaxisfreqs");
}
doCallbacksFloat64Array(fftBufferXAxis_,cfgNfft_ / 2 + 1, asynFFTXAxisId_,0);
// Add fft "plugin.fft%d.nfft"
paramName = ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_NFFT;
if( createParam(0, paramName.c_str(), asynParamInt32, &asynNfftId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter nfft");
}
setIntegerParam(asynNfftId_, (epicsInt32)cfgNfft_);
// Add fft "plugin.fft%d.rate"
paramName = ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_RATE;
if( createParam(0, paramName.c_str(), asynParamFloat64, &asynSRateId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter rate");
}
setDoubleParam(asynSRateId_, cfgDataSampleRateHz_);
// Add fft "plugin.fft%d.buffid"
paramName = ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) +
"." + ECMC_PLUGIN_ASYN_BUFF_ID;
if( createParam(0, paramName.c_str(), asynParamInt32, &asynElementsInBuffer_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter trigg");
}
setIntegerParam(asynElementsInBuffer_, (epicsInt32)elementsInBuffer_);
// Update integers
callParamCallbacks();*/
}
// Avoid issues with std:to_string()
std::string ecmcSocketCAN::to_string(int value) {
std::ostringstream os;
os << value;
return os.str();
}
asynStatus ecmcSocketCAN::writeInt32(asynUser *pasynUser, epicsInt32 value) {
int function = pasynUser->reason;
/*if( function == asynEnableId_ ) {
cfgEnable_ = value;
return asynSuccess;
} else if( function == asynFFTModeId_){
cfgMode_ = (FFT_MODE)value;// Called from low prio worker thread. Makes the hard work
void ecmcSocketCAN::doCalcWorker() {
while(true) {
doCalcEvent_.wait();
if(destructs_) {
break;
}
// Pre-process
removeDCOffset(); // Remove dc on rawdata
removeLin(); // Remove fitted line
// Process
calcFFT(); // FFT cacluation
// Post-process
scaleFFT(); // Scale FFT
calcFFTAmp(); // Calculate amplitude from complex
calcFFTXAxis(); // Calculate x axis
doCallbacksFloat64Array(rawDataBuffer_, cfgNfft_, asynRawDataId_, 0);
doCallbacksFloat64Array(prepProcDataBuffer_, cfgNfft_, asynPPDataId_, 0);
doCallbacksFloat64Array(fftBufferResultAmp_,cfgNfft_/2+1, asynFFTAmpId_, 0);
doCallbacksFloat64Array(fftBufferXAxis_, cfgNfft_/2+1, asynFFTXAxisId_,0);
callParamCallbacks();
if(cfgDbgMode_){
printComplexArray(fftBufferResult_,
cfgNfft_,
objectId_);
printEcDataArray((uint8_t*)rawDataBuffer_,
cfgNfft_*sizeof(double),
ECMC_EC_F64,
objectId_);
}
clearBuffers();
triggOnce_ = 0; // Wait for next trigger if in trigg mode
setIntegerParam(asynTriggId_,triggOnce_);
fftWaitingForCalc_ = 0;
}
}
return asynSuccess;
}
return asynError;*/
return asynSuccess;
}
asynStatus ecmcSocketCAN::readInt32(asynUser *pasynUser, epicsInt32 *value) {
int function = pasynUser->reason;
/*if( function == asynEnableId_ ) {
*value = cfgEnable_;
return asynSuccess;
} else if( function == asynFFTModeId_ ){
*value = cfgMode_;
return asynSuccess;
} else if( function == asynTriggId_ ){
*value = triggOnce_;
return asynSuccess;
}else if( function == asynFFTStatId_ ){
*value = (epicsInt32)status_;
return asynSuccess;
}else if( function == asynNfftId_ ){
*value = (epicsInt32)cfgNfft_;
return asynSuccess;
}else if( function == asynElementsInBuffer_){
*value = (epicsInt32)elementsInBuffer_;
return asynSuccess;
}
return asynError;*/
return asynSuccess;
}
asynStatus ecmcSocketCAN::readInt8Array(asynUser *pasynUser, epicsInt8 *value,
size_t nElements, size_t *nIn) {
int function = pasynUser->reason;
/*if( function == asynSourceId_ ) {
unsigned int ncopy = strlen(cfgCanIFStr_);
if(nElements < ncopy) {
ncopy = nElements;
}
memcpy (value, cfgCanIFStr_, ncopy);
*nIn = ncopy;
return asynSuccess;
}
*nIn = 0;
return asynError;*/
return asynSuccess;
}
asynStatus ecmcSocketCAN::readFloat64(asynUser *pasynUser, epicsFloat64 *value) {
int function = pasynUser->reason;
/*if( function == asynSRateId_ ) {
*value = cfgDataSampleRateHz_;
return asynSuccess;
}
return asynError;*/
return asynSuccess;
}

View File

@@ -1,136 +0,0 @@
/*************************************************************************\
* Copyright (c) 2019 European Spallation Source ERIC
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcFFTWrap.cpp
*
* Created on: Mar 22, 2020
* Author: anderssandstrom
* Credits to https://github.com/sgreg/dynamic-loading
*
\*************************************************************************/
// Needed to get headers in ecmc right...
#define ECMC_IS_PLUGIN
#include <vector>
#include <stdexcept>
#include <string>
#include "ecmcSocketCANWrap.h"
#include "ecmcSocketCAN.h"
#include "ecmcSocketCANDefs.h"
#define ECMC_PLUGIN_MAX_PORTNAME_CHARS 64
#define ECMC_PLUGIN_PORTNAME_PREFIX "PLUGIN.CAN"
static ecmcSocketCAN* can = NULL;
static char portNameBuffer[ECMC_PLUGIN_MAX_PORTNAME_CHARS];
int createSocketCAN(char* configStr, int exeSampleTimeMs) {
// create new ecmcFFT object
// create asynport name for new object ()
memset(portNameBuffer, 0, ECMC_PLUGIN_MAX_PORTNAME_CHARS);
snprintf (portNameBuffer, ECMC_PLUGIN_MAX_PORTNAME_CHARS,
ECMC_PLUGIN_PORTNAME_PREFIX);
try {
can = new ecmcSocketCAN(configStr, portNameBuffer, exeSampleTimeMs);
}
catch(std::exception& e) {
if(can) {
delete can;
}
printf("Exception: %s. Plugin will unload.\n",e.what());
return ECMC_PLUGIN_SOCKETCAN_ERROR_CODE;
}
return 0;
}
int connectSocketCAN() {
if(can){
try {
can->connectExternal();
}
catch(std::exception& e) {
printf("Exception: %s.\n",e.what());
return ECMC_PLUGIN_SOCKETCAN_ERROR_CODE;
}
}
else {
return ECMC_PLUGIN_SOCKETCAN_ERROR_CODE;
}
return 0;
}
int getSocketCANConnectd() {
if(can){
try {
return can->getConnected();
}
catch(std::exception& e) {
printf("Exception: %s.\n",e.what());
return 0;
}
}
return 0;
}
int getlastWritesError() {
if(can){
try {
return can->getlastWritesError();
}
catch(std::exception& e) {
printf("Exception: %s.\n",e.what());
return 1;
}
}
return 1;
}
int execute() {
if(can){
can->execute();
}
return 0;
}
int addWriteSocketCAN( double canId,
double len,
double data0,
double data1,
double data2,
double data3,
double data4,
double data5,
double data6,
double data7) {
if(can){
try {
return can->addWriteCAN((uint32_t) canId,
(uint8_t) len,
(uint8_t) data0,
(uint8_t) data1,
(uint8_t) data2,
(uint8_t) data3,
(uint8_t) data4,
(uint8_t) data5,
(uint8_t) data6,
(uint8_t) data7);
}
catch(std::exception& e) {
printf("Exception: %s.\n",e.what());
return ECMC_PLUGIN_SOCKETCAN_ERROR_CODE;
}
}
return ECMC_PLUGIN_SOCKETCAN_ERROR_CODE;
}
void deleteSocketCAN() {
if(can) {
delete (can);
}
}

View File

@@ -19,9 +19,9 @@ static.time:=ec_get_time();
# can_add_write(2021,0,0,0,0,0,0,0,0,0);
#can_trigg_writes();
if(can_last_writes_error()) {
println('Error during writes : ', can_last_writes_error());
};
#if(can_last_writes_error()) {
# println('Error during writes : ', can_last_writes_error());
#};
#println('Total time for one plcscan [ms]: ', (ec_get_time()-static.time)/1E6);
@@ -59,34 +59,34 @@ var byte3:=0;
#var byte7:= ec_chk_bits(dataLen,24,31);
#can_add_write(1539,8,byte0,byte1,byte2,byte3,byte4,byte5,byte6,byte7);
#can_add_write(1539,8,byte0,byte1,byte2,byte3,0,0,0,0);
can_add_write(1539,8,64,byte1,byte2,byte3,0,0,0,0);
# Acknowledge 1 toggle 0
can_add_write(1539,8,97,byte1,byte2,byte3,0,0,0,0);
# Acknowledge 2 toggle 1
can_add_write(1539,8,113,byte1,byte2,byte3,0,0,0,0);
# Acknowledge 3 toggle 0
can_add_write(1539,8,97,byte1,byte2,byte3,0,0,0,0);
# Acknowledge 4 toggle 1
can_add_write(1539,8,113,byte1,byte2,byte3,0,0,0,0);
# Acknowledge 5 toggle 0
can_add_write(1539,8,97,byte1,byte2,byte3,0,0,0,0);
# Acknowledge 6 toggle 1
can_add_write(1539,8,113,byte1,byte2,byte3,0,0,0,0);
# Acknowledge 7 toggle 0 LAST
can_add_write(1539,8,97,byte1,byte2,byte3,0,0,0,0);
# Acknowledge 6 toggle 1 Test one to much.. just resulted in one more row of unique data?!
# can_add_write(1539,8,113,byte1,byte2,byte3,0,0,0,0);
println('Total time for one plcscan [ms]: ', (ec_get_time()-static.time)/1E6);
return [];
println('NEVER HERE');
##can_add_write(1539,8,byte0,byte1,byte2,byte3,0,0,0,0);
#can_add_write(1539,8,64,byte1,byte2,byte3,0,0,0,0);
#
## Acknowledge 1 toggle 0
#can_add_write(1539,8,97,byte1,byte2,byte3,0,0,0,0);
#
## Acknowledge 2 toggle 1
#can_add_write(1539,8,113,byte1,byte2,byte3,0,0,0,0);
#
## Acknowledge 3 toggle 0
#can_add_write(1539,8,97,byte1,byte2,byte3,0,0,0,0);
#
## Acknowledge 4 toggle 1
#can_add_write(1539,8,113,byte1,byte2,byte3,0,0,0,0);
#
## Acknowledge 5 toggle 0
#can_add_write(1539,8,97,byte1,byte2,byte3,0,0,0,0);
#
## Acknowledge 6 toggle 1
#can_add_write(1539,8,113,byte1,byte2,byte3,0,0,0,0);
#
## Acknowledge 7 toggle 0 LAST
#can_add_write(1539,8,97,byte1,byte2,byte3,0,0,0,0);
#
## Acknowledge 6 toggle 1 Test one to much.. just resulted in one more row of unique data?!
## can_add_write(1539,8,113,byte1,byte2,byte3,0,0,0,0);
#
#println('Total time for one plcscan [ms]: ', (ec_get_time()-static.time)/1E6);
#return [];
#
#println('NEVER HERE');

View File

@@ -1,14 +1,55 @@
IOC_TEST:PLC-0-enable
REQMOD:mcag-trgt-muts--15408:MODULES
REQMOD:mcag-trgt-muts--15408:VERSIONS
REQMOD:mcag-trgt-muts--15408:MOD_VER
REQMOD:mcag-trgt-muts--15408:exit
REQMOD:mcag-trgt-muts--15408:BaseVersion
REQMOD:mcag-trgt-muts--15408:require_VER
REQMOD:mcag-trgt-muts--15408:ecmccfg_VER
REQMOD:mcag-trgt-muts--15408:asyn_VER
REQMOD:mcag-trgt-muts--15408:exprtk_VER
REQMOD:mcag-trgt-muts--15408:motor_VER
REQMOD:mcag-trgt-muts--15408:ecmc_VER
IOC_TEST:CAN03-V_TEMP_
IOC_TEST:CAN03-AIR_INLET_
IOC_TEST:CAN03-AIR_OUTLET_
IOC_TEST:CAN03-VrefPwrCmdCalcB1_
IOC_TEST:CAN03-VrefPwrCmdCalcB2_
IOC_TEST:CAN03-VdcCtrlCmdCalcB3_
IOC_TEST:CAN03-VdcCtrlCmdCalcB4_
IOC_TEST:CAN03-VrefPwrCmd
IOC_TEST:CAN03-VdcCtrlCmd
IOC_TEST:CAN03-BasicConfigPackArray_
REQMOD:raspberrypi-21050:exit
IOC_TEST:PLC-0-scantime
IOC_TEST:PLC-0-error
IOC_TEST:CAN03-PWR_A
IOC_TEST:CAN03-PWR_B
IOC_TEST:CAN03-PWR_OUT
IOC_TEST:CAN03-REFL_OUT
IOC_TEST:CAN03-V_REG
IOC_TEST:CAN03-V_TEMP
IOC_TEST:CAN03-I_DRV
IOC_TEST:CAN03-I_PRE
IOC_TEST:CAN03-I_1A
IOC_TEST:CAN03-I_2A
IOC_TEST:CAN03-V_REFL_SAVE
IOC_TEST:CAN03-V_PLUSMON
IOC_TEST:CAN03-V_I_DC
IOC_TEST:CAN03-I_1B
IOC_TEST:CAN03-I_2B
IOC_TEST:CAN03-V_12V_MON
IOC_TEST:CAN03-VREF_PWR_OPV
IOC_TEST:CAN03-V_AUX_IN
IOC_TEST:CAN03-V_5V_ACB
IOC_TEST:CAN03-V_3V5
IOC_TEST:CAN03-AIR_INLET
IOC_TEST:CAN03-AIR_OUTLET
REQMOD:raspberrypi-21050:BaseVersion
REQMOD:raspberrypi-21050:require_VER
REQMOD:raspberrypi-21050:ecmccfg_VER
REQMOD:raspberrypi-21050:sequencer_VER
REQMOD:raspberrypi-21050:sscan_VER
REQMOD:raspberrypi-21050:calc_VER
REQMOD:raspberrypi-21050:asyn_VER
REQMOD:raspberrypi-21050:exprtk_VER
REQMOD:raspberrypi-21050:motor_VER
REQMOD:raspberrypi-21050:ecmc_VER
REQMOD:raspberrypi-21050:ecmc_plugin_socketcan_VER
IOC_TEST:CAN03-BasicConfigB0_
IOC_TEST:PLC-0-enable
IOC_TEST:CAN03-PowerOnCmd
REQMOD:raspberrypi-21050:MODULES
REQMOD:raspberrypi-21050:VERSIONS
REQMOD:raspberrypi-21050:MOD_VER
IOC_TEST:CAN03-PDO01-Array
IOC_TEST:CAN03-SDO01-Array
IOC_TEST:CAN03-SDO02-BasicConfig

View File

@@ -21,21 +21,90 @@ $(ECMCCFG_INIT)$(SCRIPTEXEC) ${ecmccfg_DIR}startup.cmd, "IOC=$(IOC),ECMC_VER=6.3
##############################################################################
## Load plugin:
#require ecmc_plugin_advanced master # do not require then loaded twice..
require ecmc_plugin_socketcan master
epicsEnvSet(ECMC_PLUGIN_FILNAME,"/home/dev/epics/base-7.0.4/require/${E3_REQUIRE_VERSION}/siteMods/ecmc_plugin_socketcan/master/lib/${EPICS_HOST_ARCH=linux-x86_64}/libecmc_plugin_socketcan.so")
epicsEnvSet(ECMC_PLUGIN_CONFIG,"IF=can0;DBG_PRINT=1;") # Only one option implemented in this plugin
epicsEnvSet(ECMC_PLUGIN_CONFIG,"IF=vcan0;DBG_PRINT=1;") # Only one option implemented in this plugin
${SCRIPTEXEC} ${ecmccfg_DIR}loadPlugin.cmd, "PLUGIN_ID=0,FILE=${ECMC_PLUGIN_FILNAME},CONFIG='${ECMC_PLUGIN_CONFIG}', REPORT=1"
epicsEnvUnset(ECMC_PLUGIN_FILNAME)
epicsEnvUnset(ECMC_PLUGIN_CONFIG)
##############################################################################
## PLC 0
$(SCRIPTEXEC) $(ecmccfg_DIR)loadPLCFile.cmd, "PLC_ID=0, SAMPLE_RATE_MS=1000,FILE=./plc/can.plc")
# $(SCRIPTEXEC) $(ecmccfg_DIR)loadPLCFile.cmd, "PLC_ID=0, SAMPLE_RATE_MS=1000,FILE=./plc/can.plc")
##############################################################################
############# Prepare virt can for test:
# Install can utils:
# $ git clone https://github.com/linux-can/can-utils
# $ cd can-utils
# $ make
# $ make install
#
# Start virt can 0 (vcan0) and candump:
# $ sudo modprobe vcan
# $ sudo ip link add dev vcan0 type vcan
# $ sudo ip link set up vcan0
# $ candump vcan0
##############################################################################
############# Configure CAN plugin:
# Commands:
# ecmcCANOpenAddMaster -h
# Use ecmcCANOpenAddMaster(<name>, <node id>,....)
# <name> : Name of master device.
# <node id> : CANOpen node id of master.
# <LSS sample time ms> : Sample time for LSS.
# <Sync sample time ms> : Sample time for SYNC.
# <Heartbeat sample time ms> : Sample time for Heartbeat.
#
ecmcCANOpenAddMaster("ecmcCANOpenMaster",0,1000,1000,1000)
# ecmcCANOpenAddDevice -h
# Use ecmcCANOpenAddDevice(<name>, <node id>)
# <name> : Name of device.
# <node id> : CANOpen node id of device.
#
ecmcCANOpenAddDevice("testDevice",3)
# ecmcCANOpenAddPDO -h
# Use "ecmcCANOpenAddPDO(<name>, <node id>
# <name> : Name of master device.
# <node id> : CANOpen node id of device/master.
# <cob id> : CANOpen cob id of PDO.
# <dir> : Direction 1=write and 2=read.
# <ODSize> : Size of PDO (max 8 bytes).
# <readTimeoutMs> : Readtimeout in ms.
# <writeCycleMs> : Cycle time for write (if <= 0 then only write on change).
ecmcCANOpenAddPDO("status1",3,0x183,2,8,10000,0) # READ
dbLoadRecords(ecmcPluginSocketCAN_PDO_input.template, "P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1,T_SMP_MS=${ECMC_SAMPLE_RATE_MS},TSE=${ECMC_TSE},NELM=${NELM=1},CH_ID=03,DEV_ID=3,suffix=PDO01-Array,source=status1,NELM=8")
# ecmcCANOpenAddSDO -h
# Use ecmcCANOpenAddSDO(<name>, <node id>,.....)
# <name> : Name of master device.
# <node id> : CANOpen node id of device/master.
# <cob id tx> : CANOpen cob id of Tx of slave SDO.
# <cob id rx> : CANOpen cob id of Rx of slave SDO.
# <dir> : Direction 1=write and 2=read.
# <ODIndex> : OD index of SDO.
# <ODSubIndex> : OD sub index of SDO.
# <ODSize> : OS Size.
# <readTimeoutMs> : Readtimeout in ms.
#
ecmcCANOpenAddSDO("analogValues1",3,0x583,0x603,2,0x2640,0x0,56,7000) # READ
ecmcCANOpenAddSDO("analogValues2",3,0x583,0x603,2,0x2640,0x0,56,7000) # READ
ecmcCANOpenAddSDO("analogValues3",3,0x583,0x603,2,0x2640,0x0,56,7000) # READ
ecmcCANOpenAddSDO("analogValues4",3,0x583,0x603,2,0x2640,0x0,56,7000) # READ
dbLoadTemplate(ecmcPluginSocketCAN_SDO.substitutions, "P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1,T_SMP_MS=${ECMC_SAMPLE_RATE_MS},TSE=${ECMC_TSE}")
ecmcCANOpenAddSDO("basicConfig",3,0x583,0x603,1,0x2690,0x1,7,0) # WRITE
dbLoadRecords(ecmcPluginSocketCAN_SDO_output.template, "P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1,T_SMP_MS=${ECMC_SAMPLE_RATE_MS},TSE=${ECMC_TSE},NELM=${NELM=1},CH_ID=03,DEV_ID=3,suffix=BasicConfig,source=basicConfig,DTYP=asynInt8ArrayOut,FTVL=CHAR,NELM=7")
##############################################################################
############# Go to realtime:
ecmcConfigOrDie "Cfg.SetAppMode(1)"
iocInit
dbl > pvs.log
#ecmcGrepParam *plugin.can.*

209
kvaser/obsolete/README.md Normal file
View File

@@ -0,0 +1,209 @@
## History
Kvaser does not support socketcan for kernel 3.10.
However, Kvaser support supplied a custom driver for Kernel 3.10 for testing.
## Files
In this dir you will find two files:
1. socketcan_kvaser_drivers_for_3.10.tar
2. socketcan_kvaser_drivers_for_3.10_patched.tar.gz
The "socketcan_kvaser_drivers_for_3.10.tar" is the "raw" files recived from Kvaser support. This file did not compile. Kvaser suggested to remove some lines in the Makefile. This resulted in the "socketcan_kvaser_drivers_for_3.10_patched.tar.gz" file.
The "socketcan_kvaser_drivers_for_3.10_patched.tar.gz" driver compiles and installs just fine. But an error is generated when the Kvaser Leaf Light v2 interafce is connected to USB and the interface will not work:
dmesg using socketcan_kvaser_drivers_for_3.10.tar (with patched Makefile):
```
Connecting leaf to usb:
[Mar24 11:11] usbcore: deregistering interface driver kvaser_usb
[Mar24 11:12] usb 1-2: new high-speed USB device number 10 using xhci_hcd
[ +0.126425] usb 1-2: New USB device found, idVendor=0bfd, idProduct=0120, bcdDevice= 0.01
[ +0.000002] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ +0.000002] usb 1-2: Product: Kvaser Leaf Light v2
[ +0.000002] usb 1-2: Manufacturer: Kvaser AB
[ +1.058714] kvaser_usb 1-2:1.0: Cannot get software infos, error -110
[ +0.000007] kvaser_usb: probe of 1-2:1.0 failed with error -110
[ +0.000027] usbcore: registered new interface driver kvaser_usb
The interface is not working (not visible with ip addr).
Disconnecting leaf from usb:
[Mar24 11:21] usb 1-2: USB disconnect, device number 10
[Mar24 11:23] usbcore: deregistering interface driver kvaser_usb
```
## CONCLUSION
Use file "kvaser/socketcan_kvaser_drivers_1.6.113_patch_asm_3.10.tar.gz"
This file contains a patched version of the newest Kvaser socketcan drivers.
Basically everything that did not compile was removed, mostly related to CAN FD. A function can_change_state() was also not availble in kernel 3.10. Kvaser support supplied a can_change_state() which have been added to the code.
So the patched newest version of the Kvaser socketcan drivers is the only option for the time beeing. By using this driver, communiucation to a slave is possible.
One error is however encounterd during dereg of the device, see below.
dmesg from when this driver is used:
```
##########################################################################
dmesg using newest Kvaser socketcan_kvaser_drivers patched and added can_change_state()
Connecting leaf to usb:
[Mar24 11:26] usb 1-2: new high-speed USB device number 11 using xhci_hcd
[ +0.126349] usb 1-2: New USB device found, idVendor=0bfd, idProduct=0120, bcdDevice= 0.01
[ +0.000003] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ +0.000002] usb 1-2: Product: Kvaser Leaf Light v2
[ +0.000002] usb 1-2: Manufacturer: Kvaser AB
[ +0.034801] usbcore: registered new interface driver kvaser_usb
Can intreface is now visible with ip addr:
ip addr
8: can0: <NOARP,ECHO> mtu 16 qdisc noop state DOWN group default qlen 10
link/can
And the interface is working!
Disconnecting leaf from usb:
[Mar24 10:31] usb 1-2: USB disconnect, device number 8
[ +0.000050] kvaser_usb 1-2:1.0 can0: Cannot flush queue, error -19
[ +0.000002] kvaser_usb 1-2:1.0 can0: Cannot reset card, error -19
[ +0.000002] kvaser_usb 1-2:1.0 can0: Cannot stop device, error -19
```
These error messages are normal when the usb is unplugged while link is up (confirmed with kvaser support).
So conclusion is that use of "kvaser/socketcan_kvaser_drivers_1.6.113_patch_asm_3.10.tar.gz" is the only way to make it work under kernel 3.10.
According to Kvaser support this approach is OK and should work just fine. Also test shows it works well.
# OBSOLETE NOTES BELOW:
#### 1. First test without install of driver and without kvaser leaf connected.
Check support before install of driver:
```
sudo cat /boot/config-3.10.0-1127.el7.x86_64 | grep KVASER
CONFIG_CAN_KVASER_PCI=m
CONFIG_CAN_KVASER_USB=m
```
Seems support for KVASER_USB already without installing.
Check ip addr:
```
ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether c0:3f:d5:66:25:44 brd ff:ff:ff:ff:ff:ff
inet 172.30.2.34/27 brd 172.30.2.63 scope global noprefixroute dynamic eno1
valid_lft 9999557sec preferred_lft 9999557sec
inet6 fe80::c23f:d5ff:fe66:2544/64 scope link noprefixroute
valid_lft forever preferred_lft forever
```
Seems no can interface (and it's not connected).
#### 2. Connect kvaser leaf:
Check ip addr:
```
ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether c0:3f:d5:66:25:44 brd ff:ff:ff:ff:ff:ff
inet 172.30.2.34/27 brd 172.30.2.63 scope global noprefixroute dynamic eno1
valid_lft 9999557sec preferred_lft 9999557sec
inet6 fe80::c23f:d5ff:fe66:2544/64 scope link noprefixroute
valid_lft forever preferred_lft forever
```
Still no can0...
```
dmesg:[ 543.068096] usb 2-4: new high-speed USB device number 4 using xhci_hcd
[ 543.191792] usb 2-4: New USB device found, idVendor=0bfd, idProduct=0120, bcdDevice= 0.01
[ 543.191803] usb 2-4: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 543.191809] usb 2-4: Product: Kvaser Leaf Light v2
[ 543.191815] usb 2-4: Manufacturer: Kvaser AB
```
#### 3. Try start services and conf if
###### Services
Start services:
```
$ sudo modprobe can_dev
$ sudo modprobe can
$ sudo modprobe can_raw
$ sudo ip link set can0 type can bitrate 125000
```
Conclusion:
Cannot find can0.. Need to install kvaser driver..
#### Insall kvaser socketcan 3.10
##### 1 unplug kvaser leaf
##### 2 untar driver source
```
cd kvaser
tar -xvf socketcan_kvaser_drivers_for_3.10.tar
cd socketcan_kvaser_drivers/
```
NOTE: The socketcan_kvaser_drivers_for_3.10.tar was recived on request from kvaser support. Their normal socketcan driver will not support kernel 3.10.
##### 3 make
Read the README file in kvaser driver.
These commands are from that README:
```
$ sudo make uninstall
$ make
$ sudo make install
```
Fails at make...
sudo yum install kernel-devel
The kernel sources are at the wrong place.. Sources for wrong kernel!!
Need to update
```
sudo yum update
```
Now the kernel sources are at the corerct location. patched version of new socketcan version builds and seems to work with candump and connected pmu905
#### THIS IS A SIDE NOTE for PEAK usb CAN interface !!!
Check if support for PEAK USB
```
sudo cat /boot/config-3.10.0-1127.el7.x86_64 | grep PEAK
CONFIG_CAN_PEAK_PCI=m
CONFIG_CAN_PEAK_PCIEC=y
CONFIG_CAN_PEAK_USB=m
CONFIG_SND_FIREWIRE_SPEAKERS=m
# CONFIG_SPEAKUP is not set
```

Binary file not shown.

126
kvaser/readme.md Normal file
View File

@@ -0,0 +1,126 @@
# Use Kvaser Leaf Light v2
## Kernel 3.10
The socketcan drivers sources downloadable from kvaser webpage will not work for kernel 3.10 but a patched version can be used.
NOTE: This workflow will only work for the Kvaser Leaf Light v2 (and not the Kvaser Hydra).
### Pre reqs
Install kernel headers
```
sudo yum install kernel-devel
```
Note: make sure that correct kernel headers are installed for the current kernel, otherwise maybe a update is needed.
### Install kvaser driver
#### Use the correct source
Goto kvaser dir and untar the file: socketcan_kvaser_drivers_1.6.113_patch_asm_3.10.tar.gz
The "socketcan_kvaser_drivers_1.6.113_patch_asm_3.10.tar.gz" contains a patched version of the kvaser socketcan driver version 1.6.113.
Functionality not availbale in kernel 3.10 have been removed. For the driver for the Leaf Light v2 this includes:
1. Removing CAN FD support
2. Adding sources provided by kvaser support for the "can_change_state()" function since it is not avilabe in the 3.10 kernel.
NOTE: This source will only work for Kvaser Leaf Light v2 usb. It will not work for Kvaser Hydra.
```
cd kvaser
tar -xvf socketcan_kvaser_drivers_1.6.113_patch_asm_3.10.tar.gz
cd socketcan_kvaser_drivers/
```
Note: Non patched source downladed from kvaser will not work with kernel 3.10. So use the source in kvaser dir of this repo.
#### make
Read the README file in kvaser driver.
These commands are from that README:
```
# UNPLUGG KVASER USB DEVICE FIRST
$ sudo make uninstall
$ make
$ sudo make install
```
Note: You will get some warnings from hydra source but like stated above the hydra hw or driver is NOT supported and should be avoided. So basically this driver ONLY supports the Kvaser Leaf Light v2.
### DMESG
#### Connect Kvaser Leaf Light v2 USB
dmesg using newest Kvaser socketcan_kvaser_drivers patched and added "can_change_state()" (socketcan_kvaser_drivers_1.6.113_patch_asm_3.10.tar.gz):
```
# Connecting leaf to usb:
[Mar24 11:26] usb 1-2: new high-speed USB device number 11 using xhci_hcd
[ +0.126349] usb 1-2: New USB device found, idVendor=0bfd, idProduct=0120, bcdDevice= 0.01
[ +0.000003] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ +0.000002] usb 1-2: Product: Kvaser Leaf Light v2
[ +0.000002] usb 1-2: Manufacturer: Kvaser AB
[ +0.034801] usbcore: registered new interface driver kvaser_usb
Can interface is now visible with ip addr:
ip addr
8: can0: <NOARP,ECHO> mtu 16 qdisc noop state DOWN group default qlen 10
link/can
```
The interface is working and communication to can slave are possible.
#### Disconnect Kvaser Leaf Light v2 USB
The following errors will appear in dmesg if unplugging the device while link is up.
```
# Disconnecting leaf from usb:
[Mar24 10:31] usb 1-2: USB disconnect, device number 8
[ +0.000050] kvaser_usb 1-2:1.0 can0: Cannot flush queue, error -19
[ +0.000002] kvaser_usb 1-2:1.0 can0: Cannot reset card, error -19
[ +0.000002] kvaser_usb 1-2:1.0 can0: Cannot stop device, error -19
```
Checked with kvaser support and this is normal when unplugging the usb when the the link is up. So this is not an Issue.
### More info
More info on other driver supplied by Kvaser can be found here:
[Other driver (currently not working) ](obsolete/README.md)
## Kernel version >= 4.x
Go to kvaser webpage and download kvaser socket can drivers. Install according to instructions.
## Start services and configure interface
First connect the Kvaser Leaf Light v2 usb interface to the controller.
Start services:
```
$ sudo modprobe can_dev
$ sudo modprobe can
$ sudo modprobe can_raw
#$ sudo modprobe kvaser_usb # not needed, will start as soon as leaf is plugged in
$ sudo ip link set can0 type can bitrate 125000 # 125000 is bitrate (works for pmu905)
$ sudo ip link set up can0
```
Now you should see the can0 interface, test with "ip addr"
```
ip addr
...
7: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN group default qlen 10
link/can
...
```
Check that you have the correct services running (lsmod):
```
lsmod | grep can
can_raw 17120 0
can 36567 1 can_raw
can_dev 20760 1 kvaser_usb
...
```

171
src/ecmcByteToArrayAsub.cpp Normal file
View File

@@ -0,0 +1,171 @@
/*************************************************************************\
* Copyright (c) 2019 European Spallation Source ERIC
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcByteToArrayAsub.cpp
*
* Created on: Mar 18, 2021
* Author: anderssandstrom
*
* Usage:
* 1. Link bytes to inputs A..S.
* 2. Link array to VALA.
* 3. Set size of output in NOVA. This also defines how many inputs will be used.
* Note: Max 18 bytes (input A..S) will be merged into the array.
*
\*************************************************************************/
// aSub, EPICS related headers
#include <aSubRecord.h>
#include <registryFunction.h>
#include <epicsExport.h>
// std::cout
#include <iostream>
// split double into fractional and integer
#include <math.h>
#include <string.h>
// declare init function
static long ecmcByteToArrayInit(struct aSubRecord *rec);
epicsRegisterFunction(ecmcByteToArrayInit);
// declare worker function
static long ecmcByteToArray(struct aSubRecord *rec);
epicsRegisterFunction(ecmcByteToArray);
// init (INAM)
static long ecmcByteToArrayInit(struct aSubRecord *rec){
epicsUInt8 byteCount=(epicsUInt8)rec->nova;
std::cout << "ecmcByteToArrayInit aSubRecord: "<< rec->name << std::endl;
printf("ecmcByteToArray: Bytes to me merged %d\n",(int)byteCount);
return 0;
}
static long ecmcByteToArray(struct aSubRecord *rec){
// input A must be size of output array
epicsUInt8 byteCount=(epicsUInt8)rec->nova;
//printf("ecmcByteToArray: Meging %d bytes to an array.\n",(int)byteCount);
// Max 18 byte in a row
if(byteCount <=0 || byteCount>18){
printf("WARNING: Only 18 first bytes will be transferred to output.\n");
}
epicsUInt8 *vala;
vala = (epicsUInt8 *)rec->vala;
if(byteCount >= 1 && rec->noa ==1) {
vala[0]=*(epicsUInt8 *)rec->a;
}
if(byteCount >= 2 && rec->nob ==1) {
vala[1]=*(epicsUInt8 *)rec->b;
}
if(byteCount >= 3 && rec->noc ==1) {
vala[2]=*(epicsUInt8 *)rec->c;
}
if(byteCount >= 4 && rec->nod ==1) {
vala[3]=*(epicsUInt8 *)rec->d;
}
if(byteCount >= 5 && rec->noe ==1) {
vala[4]=*(epicsUInt8 *)rec->e;
}
if(byteCount >= 6 && rec->nof ==1) {
vala[5]=*(epicsUInt8 *)rec->f;
}
if(byteCount >= 7 && rec->nog ==1) {
vala[6]=*(epicsUInt8 *)rec->g;
}
if(byteCount >= 8 && rec->noh ==1) {
vala[7]=*(epicsUInt8 *)rec->h;
}
if(byteCount >= 9 && rec->noi ==1) {
vala[8]=*(epicsUInt8 *)rec->i;
}
if(byteCount >= 10 && rec->noj ==1) {
vala[9]=*(epicsUInt8 *)rec->j;
}
if(byteCount >= 11 && rec->nok ==1) {
vala[10]=*(epicsUInt8 *)rec->k;
}
if(byteCount >= 12 && rec->nol ==1) {
vala[11]=*(epicsUInt8 *)rec->l;
}
if(byteCount >= 13 && rec->nom ==1) {
vala[12]=*(epicsUInt8 *)rec->m;
}
if(byteCount >= 13 && rec->non ==1) {
vala[13]=*(epicsUInt8 *)rec->n;
}
if(byteCount >= 14 && rec->noo ==1) {
vala[14]=*(epicsUInt8 *)rec->o;
}
if(byteCount >= 15 && rec->nop ==1) {
vala[15]=*(epicsUInt8 *)rec->p;
}
if(byteCount >= 16 && rec->noq ==1) {
vala[16]=*(epicsUInt8 *)rec->q;
}
if(byteCount >= 17 && rec->nor ==1) {
vala[17]=*(epicsUInt8 *)rec->r;
}
if(byteCount >= 18 && rec->nos ==1) {
vala[18]=*(epicsUInt8 *)rec->s;
}
return 0;
}
/*
--------------------------------------------------------------------------------
EPICS database example
record(aSub, "$(P)CAN${CH_ID}-BasicConfigPackArray_") {
field(INAM, "ecmcByteToArrayInit")
field(SNAM, "ecmcByteToArray")
field(FTA, "UCHAR")
field(NOA, "1")
field(INPA, "$(P)CAN${CH_ID}-BasicConfigB0_.VAL") # Byte 0
field(FTB, "UCHAR")
field(NOB, "1")
field(INPB, "$(P)CAN${CH_ID}-VrefPwrCmdCalcB1_.VAL") # Byte 1
field(FTC, "UCHAR")
field(NOC, "1")
field(INPC, "$(P)CAN${CH_ID}-VrefPwrCmdCalcB2_.VAL") # Byte 2
field(FTD, "UCHAR")
field(NOD, "1")
field(INPD, "$(P)CAN${CH_ID}-VdcCtrlCmdCalcB3_.VAL") # Byte 3
field(FTE, "UCHAR")
field(NOE, "1")
field(INPE, "$(P)CAN${CH_ID}-VdcCtrlCmdCalcB4_.VAL") # Byte 4
field(FTF, "UCHAR")
field(NOF, "1")
field(INPF, "0") # Byte 5
field(FTG, "UCHAR")
field(NOG, "1")
field(INPG, "0") # Byte 6
field(FTVA, "UCHAR")
field(OUTA, "$(P)CAN${CH_ID}-SDO02-BasicConfig")
field(NOVA, "7") # 7 bytes (0..6 corresponds to input A..G)
field(FLNK, "$(P)CAN${CH_ID}-SDO02-BasicConfig.PROC") # Send the data
}
--------------------------------------------------------------------------------
*/

267
src/ecmcCANOpenDevice.cpp Normal file
View File

@@ -0,0 +1,267 @@
/*************************************************************************\
* Copyright (c) 2019 European Spallation Source ERIC
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcCANOpenDevice.cpp
*
* Created on: Mar 08, 2021
* Author: anderssandstrom
*
\*************************************************************************/
// Needed to get headers in ecmc right...
#define ECMC_IS_PLUGIN
#include <sstream>
#include "ecmcCANOpenDevice.h"
#include "ecmcAsynPortDriver.h"
#include "ecmcPluginClient.h"
/**
* ecmc ecmcCANOpenDevice class
*/
ecmcCANOpenDevice::ecmcCANOpenDevice(ecmcSocketCANWriteBuffer* writeBuffer,
uint32_t nodeId, // 0x580 + CobId
int exeSampleTimeMs,
const char* name,
int heartTimeoutMs,
int dbgMode) {
writeBuffer_ = writeBuffer;
nodeId_ = nodeId;
exeSampleTimeMs_ = exeSampleTimeMs;
exeCounter_ = 0;
errorCode_ = 0;
heartTimeoutMs_ = heartTimeoutMs;
dbgMode_ = dbgMode;
name_ = strdup(name);
isMaster_ = false;
nmtState_ = NMT_NOT_VALID;
nmtStateOld_ = NMT_NOT_VALID;
nmtActParam_ = NULL;
heartBeatCounter_ = 0;
pdoCounter_ = 0;
sdoCounter_ = 0;
sdo1Lock_.test_and_set(); // make sure only one sdo is accessing the bus at the same time
sdo1Lock_.clear();
for(int i = 0 ; i<ECMC_CAN_DEVICE_PDO_MAX_COUNT;i++) {
pdos_[i] = NULL;
}
for(int i = 0 ; i<ECMC_CAN_DEVICE_SDO_MAX_COUNT;i++) {
sdos_[i] = NULL;
}
initAsyn();
}
ecmcCANOpenDevice::~ecmcCANOpenDevice() {
for(int i = 0 ; i<ECMC_CAN_DEVICE_PDO_MAX_COUNT;i++) {
delete pdos_[i];
}
for(int i = 0 ; i<ECMC_CAN_DEVICE_SDO_MAX_COUNT;i++) {
delete sdos_[i];
}
free(name_);
}
void ecmcCANOpenDevice::execute() {
exeCounter_++;
for(int i=0 ; i<pdoCounter_; i++) {
if(pdos_[i]) {
pdos_[i]->execute();
}
}
for(int i=0 ; i<sdoCounter_; i++) {
if(sdos_[i]) {
sdos_[i]->execute();
}
}
// NMT hearbeat timout
if (heartBeatCounter_ * exeSampleTimeMs_ >= heartTimeoutMs_) {
nmtStateOld_ = nmtState_;
nmtState_ = NMT_NOT_VALID;
nmtActParam_->refreshParam(1);
}
else {
heartBeatCounter_ ++;
}
return;
}
// new rx frame recived!
void ecmcCANOpenDevice::newRxFrame(can_frame *frame) {
// only validate if not master
if (!validateFrame(frame) && !isMaster_) {
return;
}
// forward to pdos
for(int i=0 ; i<pdoCounter_; i++) {
if(pdos_[i]) {
pdos_[i]->newRxFrame(frame);
}
}
// forward to sdos
for(int i=0 ; i<sdoCounter_; i++) {
if(sdos_[i]) {
sdos_[i]->newRxFrame(frame);
}
}
// NMT
if(!isMaster_ && nmtActParam_) {
checkNMT(frame);
}
return;
}
// r 0x183 [8] 0x00 0x00 0x00 0x00 0x0B 0x40 0x04 0x20
int ecmcCANOpenDevice::validateFrame(can_frame *frame) {
// nodeid is always lower 7bits.. Need to check this calc.. byte order?!
uint8_t tempNodeId = frame->can_id & 0x7F;
if(tempNodeId != nodeId_) {
return 0;
}
return 1;
}
int ecmcCANOpenDevice::checkNMT(can_frame *frame) {
// check if NMT frame
if(frame->can_id == (ECMC_CANOPEN_NMT_BASE + nodeId_)) {
if(frame->can_dlc == 1){
switch(frame->data[0]) {
case ECMC_CANOPEN_NMT_BOOT:
nmtState_ = NMT_BOOT_UP;
break;
case ECMC_CANOPEN_NMT_STOP:
nmtState_ = NMT_STOPPED;
break;
case ECMC_CANOPEN_NMT_OP:
nmtState_ = NMT_OP;
break;
case ECMC_CANOPEN_NMT_PREOP:
nmtState_ = NMT_BOOT_UP;
break;
default:
nmtState_ = NMT_NOT_VALID;
break;
}
}
heartBeatCounter_ = 0;
if(nmtState_ != nmtStateOld_) {
nmtActParam_->refreshParam(1);
}
nmtStateOld_ = nmtState_;
}
return 0;
}
int ecmcCANOpenDevice::addPDO(uint32_t cobId,
ecmc_can_direction rw,
uint32_t ODSize,
int readTimeoutMs,
int writeCycleMs, //if <0 then write on demand..
const char* name) {
if(pdoCounter_>= ECMC_CAN_DEVICE_PDO_MAX_COUNT) {
return ECMC_CAN_PDO_INDEX_OUT_OF_RANGE;
}
pdos_[pdoCounter_] = new ecmcCANOpenPDO(writeBuffer_,
nodeId_,
cobId,
rw,
ODSize,
readTimeoutMs,
writeCycleMs,
exeSampleTimeMs_,
name,
dbgMode_);
pdoCounter_++;
return 0;
}
int ecmcCANOpenDevice::addSDO(uint32_t cobIdTx, // 0x580 + CobId
uint32_t cobIdRx, // 0x600 + Cobid
ecmc_can_direction rw,
uint16_t ODIndex, // Object dictionary index
uint8_t ODSubIndex, // Object dictionary subindex
uint32_t ODSize,
int readSampleTimeMs,
const char* name) {
if(sdoCounter_>= ECMC_CAN_DEVICE_SDO_MAX_COUNT) {
return ECMC_CAN_SDO_INDEX_OUT_OF_RANGE;
}
sdos_[sdoCounter_] = new ecmcCANOpenSDO(writeBuffer_,
nodeId_,
cobIdTx,
cobIdRx,
rw,
ODIndex,
ODSubIndex,
ODSize,
readSampleTimeMs,
exeSampleTimeMs_,
name,
&sdo1Lock_,
sdoCounter_,
dbgMode_);
sdoCounter_++;
return 0;
}
uint32_t ecmcCANOpenDevice::getNodeId() {
return nodeId_;
}
void ecmcCANOpenDevice::initAsyn() {
ecmcAsynPortDriver *ecmcAsynPort = (ecmcAsynPortDriver *)getEcmcAsynPortDriver();
if(!ecmcAsynPort) {
printf("ERROR: ecmcAsynPort NULL.");
throw std::runtime_error( "ERROR: ecmcAsynPort NULL." );
}
// Add resultdata "plugin.can.dev%d.<name>"
std::string paramName = ECMC_PLUGIN_ASYN_PREFIX + std::string(".dev") +
to_string(nodeId_) + ".nmtstate";
nmtActParam_ = ecmcAsynPort->addNewAvailParam(
paramName.c_str(), // name
asynParamInt32, // asyn type
(uint8_t*)&nmtState_, // pointer to data
sizeof(nmtState_), // size of data
ECMC_EC_S32, // ecmc data type
0); // die if fail
if(!nmtActParam_) {
printf("ERROR: Failed create asyn param for NMT state.");
throw std::runtime_error( "ERROR: Failed create asyn param for data: " + paramName);
}
nmtActParam_->addSupportedAsynType(asynParamUInt32Digital);
nmtActParam_->refreshParam(1); // read once into asyn param lib
ecmcAsynPort->callParamCallbacks(ECMC_ASYN_DEFAULT_LIST, ECMC_ASYN_DEFAULT_ADDR);
}
// Avoid issues with std:to_string()
std::string ecmcCANOpenDevice::to_string(int value) {
std::ostringstream os;
os << value;
return os.str();
}

95
src/ecmcCANOpenDevice.h Normal file
View File

@@ -0,0 +1,95 @@
/*************************************************************************\
* Copyright (c) 2019 European Spallation Source ERIC
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcCANOpenDevice.h
*
* Created on: Mar 08, 2021
* Author: anderssandstrom
*
\*************************************************************************/
#ifndef ECMC_CANOPEN_DEVICE_H_
#define ECMC_CANOPEN_DEVICE_H_
#include <stdexcept>
#include <atomic>
#include "ecmcDataItem.h"
#include "ecmcAsynPortDriver.h"
#include "ecmcSocketCANDefs.h"
#include "ecmcCANOpenPDO.h"
#include "ecmcCANOpenSDO.h"
#include "inttypes.h"
#include <string>
#include "ecmcSocketCANWriteBuffer.h"
#include "epicsMutex.h"
#include <linux/can.h>
#include <linux/can/raw.h>
#define ECMC_CAN_DEVICE_PDO_MAX_COUNT 8
#define ECMC_CAN_DEVICE_SDO_MAX_COUNT 8
#define ECMC_CAN_ERROR_PDO_TIMEOUT 100
#define ECMC_CAN_PDO_INDEX_OUT_OF_RANGE 1000
#define ECMC_CAN_SDO_INDEX_OUT_OF_RANGE 1001
class ecmcCANOpenDevice {
public:
ecmcCANOpenDevice(ecmcSocketCANWriteBuffer* writeBuffer,
uint32_t nodeId,
int exeSampleTimeMs,
const char* name,
int heartTimeoutMs,
int dbgMode);
virtual ~ecmcCANOpenDevice();
void execute();
void newRxFrame(can_frame *frame);
int addPDO(uint32_t cobId,
ecmc_can_direction rw,
uint32_t ODSize,
int readTimeoutMs,
int writeCycleMs, //if <0 then write on demand.
const char* name);
int addSDO(uint32_t cobIdTx, // 0x580 + CobId
uint32_t cobIdRx, // 0x600 + Cobid
ecmc_can_direction rw,
uint16_t ODIndex, // Object dictionary index
uint8_t ODSubIndex, // Object dictionary subindex
uint32_t ODSize,
int readSampleTimeMs,
const char* name);
uint32_t getNodeId();
protected:
int validateFrame(can_frame *frame);
int checkNMT(can_frame *frame);
ecmcSocketCANWriteBuffer *writeBuffer_;
uint32_t nodeId_; // with cobid
int exeSampleTimeMs_;
int exeCounter_;
int errorCode_;
int dbgMode_;
int pdoCounter_;
int sdoCounter_;
char* name_;
int heartTimeoutMs_;
int heartBeatCounter_;
ecmcCANOpenPDO *pdos_[ECMC_CAN_DEVICE_PDO_MAX_COUNT];
ecmcCANOpenSDO *sdos_[ECMC_CAN_DEVICE_SDO_MAX_COUNT];
bool isMaster_;
std::atomic_flag sdo1Lock_;
ecmc_nmt_state_act nmtState_;
ecmc_nmt_state_act nmtStateOld_;
//ASYN
void initAsyn();
ecmcAsynDataItem *nmtActParam_;
std::string to_string(int value);
};
#endif /* ECMC_CANOPEN_DEVICE_H_ */

99
src/ecmcCANOpenMaster.cpp Normal file
View File

@@ -0,0 +1,99 @@
/*************************************************************************\
* Copyright (c) 2019 European Spallation Source ERIC
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcCANOpenMaster.cpp
*
* Created on: Mar 09, 2021
* Author: anderssandstrom
*
\*************************************************************************/
// Needed to get headers in ecmc right...
#define ECMC_IS_PLUGIN
#include <sstream>
#include "ecmcCANOpenMaster.h"
/**
* ecmc ecmcCANOpenMaster class
*/
ecmcCANOpenMaster::ecmcCANOpenMaster(ecmcSocketCANWriteBuffer* writeBuffer,
uint32_t nodeId,
int exeSampleTimeMs,
int lssSampleTimeMs,
int syncSampleTimeMs,
int heartSampleTimeMs,
const char* name,
int dbgMode):
ecmcCANOpenDevice(writeBuffer,
nodeId, // 0x580 + CobId
exeSampleTimeMs,
name,
0, //NMT timeout is 0 for master (always OP)
dbgMode) {
lssPdo_ = NULL;
syncPdo_ = NULL;
heartPdo_ = NULL;
isMaster_ = true;
lssSampleTimeMs_ = lssSampleTimeMs;
syncSampleTimeMs_ = syncSampleTimeMs;
heartSampleTimeMs_ = heartSampleTimeMs;
// Master is always in OP
if(nmtActParam_) {
nmtState_ = NMT_OP;
nmtStateOld_ = NMT_OP;
nmtActParam_->refreshParam(1);
}
int errorCode = 0;
// lssPdo_ = new ecmcCANOpenPDO( writeBuffer_, 0x7E5,DIR_WRITE,0,0,1000,exeSampleTimeMs_,"lss", cfgDbgMode_);
errorCode = addPDO(0x7E5, // uint32_t cobId,
DIR_WRITE, // ecmc_can_direction rw,
0, // uint32_t ODSize,
0, // int readTimeoutMs,
lssSampleTimeMs, // int writeCycleMs, if < 0 then write on demand.
"lss"); // const char* name);
if(errorCode) {
throw std::runtime_error( "LSS PDO NULL.");
}
lssPdo_ = pdos_[pdoCounter_-1];
// Test sync signal
// can0 0x80 [0]
// syncPdo_ = new ecmcCANOpenPDO( writeBuffer_, 0x80,DIR_WRITE,0,0,1000,exeSampleTimeMs_,"sync", cfgDbgMode_);
errorCode = addPDO(0x80, // uint32_t cobId,
DIR_WRITE, // ecmc_can_direction rw,
0, // uint32_t ODSize,
0, // int readTimeoutMs,
syncSampleTimeMs, // int writeCycleMs, if < 0 then write on demand.
"sync"); // const char* name);
if(errorCode) {
throw std::runtime_error( "Sync PDO NULL.");
}
syncPdo_ = pdos_[pdoCounter_-1];
// Test heartbeat signal
// can0 0x701 [1] 05
//can_add_write(1793,1,5,0,0,0,0,0,0,0);
//heartPdo_ = new ecmcCANOpenPDO( writeBuffer_, 0x701,DIR_WRITE,1,0,1000,exeSampleTimeMs_,"heartbeat",cfgDbgMode_);
//heartPdo_->setValue(5);
errorCode = addPDO(0x700+nodeId_, // uint32_t cobId,
DIR_WRITE, // ecmc_can_direction rw,
1, // uint32_t ODSize,
0, // int readTimeoutMs,
heartSampleTimeMs, // int writeCycleMs, if < 0 then write on demand.
"heart"); // const char* name);
if(errorCode) {
throw std::runtime_error( "Heart PDO NULL.");
}
heartPdo_ = pdos_[pdoCounter_-1];
heartPdo_->setValue(5);
}
ecmcCANOpenMaster::~ecmcCANOpenMaster() {
}

52
src/ecmcCANOpenMaster.h Normal file
View File

@@ -0,0 +1,52 @@
/*************************************************************************\
* Copyright (c) 2019 European Spallation Source ERIC
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcCANOpenMaster.h
*
* Created on: Mar 08, 2021
* Author: anderssandstrom
*
\*************************************************************************/
#ifndef ECMC_CANOPEN_MASTER_H_
#define ECMC_CANOPEN_MASTER_H_
#include <stdexcept>
#include "ecmcDataItem.h"
#include "ecmcAsynPortDriver.h"
#include "ecmcSocketCANDefs.h"
#include "ecmcCANOpenPDO.h"
#include "ecmcCANOpenSDO.h"
#include "ecmcCANOpenDevice.h"
#include "inttypes.h"
#include <string>
#include "ecmcSocketCANWriteBuffer.h"
#include "epicsMutex.h"
#include <linux/can.h>
#include <linux/can/raw.h>
class ecmcCANOpenMaster : public ecmcCANOpenDevice {
public:
ecmcCANOpenMaster(ecmcSocketCANWriteBuffer* writeBuffer,
uint32_t nodeId,
int exeSampleTimeMs,
int lssSampleTimeMs,
int syncSampleTimeMs,
int heartSampleTimeMs,
const char* name,
int dbgMode);
~ecmcCANOpenMaster();
private:
ecmcCANOpenPDO *lssPdo_;
ecmcCANOpenPDO *syncPdo_;
ecmcCANOpenPDO *heartPdo_;
int lssSampleTimeMs_;
int syncSampleTimeMs_;
int heartSampleTimeMs_;
};
#endif /* ECMC_CANOPEN_MASTER_H_ */

268
src/ecmcCANOpenPDO.cpp Normal file
View File

@@ -0,0 +1,268 @@
/*************************************************************************\
* Copyright (c) 2019 European Spallation Source ERIC
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcCANOpenPDO.cpp
*
* Created on: Mar 22, 2020
* Author: anderssandstrom
*
\*************************************************************************/
// Needed to get headers in ecmc right...
#define ECMC_IS_PLUGIN
#include <sstream>
#include "ecmcCANOpenPDO.h"
#include "ecmcAsynPortDriver.h"
#include "ecmcPluginClient.h"
// Calback function to init write from asyn
asynStatus asynWritePDOValue(void* data, size_t bytes, asynParamType asynParType,void *userObj) {
// userobj = NULL
if(!userObj) {
printf("Error: asynWritePDOValue() fail, no user obj defined.\n");
return asynError;
}
ecmcCANOpenPDO* pdo = (ecmcCANOpenPDO*)userObj;
int bytesToCp = bytes;
if (bytes > 8) {
bytesToCp = 8;
}
uint64_t tempData = 0;
memcpy(&tempData,data,bytesToCp);
pdo->setValue(tempData);
pdo->writeValue();
return asynSuccess;
}
/**
* ecmc ecmcCANOpenPDO class
*/
ecmcCANOpenPDO::ecmcCANOpenPDO(ecmcSocketCANWriteBuffer* writeBuffer,
uint32_t nodeId,
uint32_t cobId, // 0x580 + CobId
ecmc_can_direction rw,
uint32_t ODSize,
int readTimeoutMs,
int writeCycleMs,
int exeSampleTimeMs,
const char* name,
int dbgMode) {
writeBuffer_ = writeBuffer;
nodeId_ = nodeId;
cobId_ = cobId;
ODSize_ = ODSize;
name_ = strdup(name);
if(ODSize_ > 8) {
ODSize_ = 8;
}
readTimeoutMs_ = readTimeoutMs;
writeCycleMs_ = writeCycleMs;
exeSampleTimeMs_ = exeSampleTimeMs;
rw_ = rw;
exeCounter_ = 0;
busy_ = 0;
errorCode_ = 0;
dataBuffer_ = new uint8_t[ODSize_];
memset(dataBuffer_,0,ODSize_);
dbgMode_ = dbgMode;
refreshNeeded_ = 0;
writeFrame_.can_id = cobId_;
writeFrame_.can_dlc = ODSize; // data length
writeFrame_.data[0] = 0; // request read cmd
writeFrame_.data[1] = 0;
writeFrame_.data[2] = 0;
writeFrame_.data[3] = 0;
writeFrame_.data[4] = 0;
writeFrame_.data[5] = 0;
writeFrame_.data[6] = 0;
writeFrame_.data[7] = 0;
dataMutex_ = epicsMutexCreate();
initAsyn();
}
ecmcCANOpenPDO::~ecmcCANOpenPDO() {
delete[] dataBuffer_;
free(name_);
}
void ecmcCANOpenPDO::execute() {
exeCounter_++;
if(rw_ == DIR_READ) {
if(exeCounter_* exeSampleTimeMs_ >= readTimeoutMs_) {
errorCode_ = ECMC_CAN_ERROR_PDO_TIMEOUT;
if(dbgMode_) {
printf("ECMC_CAN_ERROR_PDO_TIMEOUT (0x%x)\n",errorCode_);
}
refreshNeeded_ = 1;
exeCounter_ = 0;
}
}
else { //DIR_WRITE
if(writeCycleMs_<=0) { // Only write on demand if cycle is less than 0
exeCounter_ = 0;
return;
}
if(exeCounter_* exeSampleTimeMs_ >= writeCycleMs_) {
writeValue(); // write in defined cycle
exeCounter_ = 0;
}
}
// Refresh in sync with ecmc
refreshAsynParams();
}
// new rx frame recived!
void ecmcCANOpenPDO::newRxFrame(can_frame *frame) {
// Wait for:
if(rw_ == DIR_READ) {
if(validateFrame(frame)) {
epicsMutexLock(dataMutex_);
memset(dataBuffer_,0,ODSize_);
memcpy(dataBuffer_, &(frame->data[0]),frame->can_dlc);
epicsMutexUnlock(dataMutex_);
refreshNeeded_ = 1;
errorCode_ = 0;
refreshNeeded_ = 1;
if(dbgMode_) {
printBuffer();
}
}
}
}
void ecmcCANOpenPDO::printBuffer() {
if(!dataBuffer_) {
return;
}
for(uint32_t i = 0; i < ODSize_; i = i + 2) {
uint16_t test;
memcpy(&test,&dataBuffer_[i],2);
printf("data[%02d]: %u\n",i/2,test);
}
}
// r 0x183 [8] 0x00 0x00 0x00 0x00 0x0B 0x40 0x04 0x20
int ecmcCANOpenPDO::validateFrame(can_frame *frame) {
if(frame->can_id != cobId_) {
return 0;
}
if(frame->can_dlc != ODSize_) {
return 0;
}
return 1;
}
void ecmcCANOpenPDO::setValue(uint64_t data) {
epicsMutexLock(dataMutex_);
memcpy(dataBuffer_, &data, ODSize_);
epicsMutexUnlock(dataMutex_);
}
int ecmcCANOpenPDO::writeValue() {
if(writeFrame_.can_dlc > 0) {
epicsMutexLock(dataMutex_);
memcpy(&(writeFrame_.data[0]), dataBuffer_ ,writeFrame_.can_dlc);
epicsMutexUnlock(dataMutex_);
}
int errorCode = writeBuffer_->addWriteCAN(&writeFrame_);
refreshNeeded_ = 1;
return errorCode;
}
void ecmcCANOpenPDO::initAsyn() {
ecmcAsynPortDriver *ecmcAsynPort = (ecmcAsynPortDriver *)getEcmcAsynPortDriver();
if(!ecmcAsynPort) {
printf("ERROR: ecmcAsynPort NULL.");
throw std::runtime_error( "ERROR: ecmcAsynPort NULL." );
}
// Add resultdata "plugin.can.dev%d.<name>"
std::string paramName = ECMC_PLUGIN_ASYN_PREFIX + std::string(".dev") +
to_string(nodeId_) + ".pdo" /*+ to_string(objIndex_) */
+ "." + std::string(name_);
dataParam_ = ecmcAsynPort->addNewAvailParam(
paramName.c_str(), // name
asynParamInt8Array, // asyn type
dataBuffer_, // pointer to data
ODSize_, // size of data
ECMC_EC_U8, // ecmc data type
0); // die if fail
if(!dataParam_) {
printf("ERROR: Failed create asyn param for data.");
throw std::runtime_error( "ERROR: Failed create asyn param for data: " + paramName);
}
// Allow different types depending on size
if(ODSize_>1){
dataParam_->addSupportedAsynType(asynParamInt16Array);
}
if(ODSize_>3){
dataParam_->addSupportedAsynType(asynParamInt32Array);
dataParam_->addSupportedAsynType(asynParamFloat32Array);
dataParam_->addSupportedAsynType(asynParamInt32);
}
if(ODSize_>7){
dataParam_->addSupportedAsynType(asynParamFloat64Array);
dataParam_->addSupportedAsynType(asynParamFloat64);
}
dataParam_->setAllowWriteToEcmc(rw_ == DIR_WRITE);
if(rw_ == DIR_WRITE) {
dataParam_->setExeCmdFunctPtr(asynWritePDOValue,this);
}
dataParam_->refreshParam(1); // read once into asyn param lib
ecmcAsynPort->callParamCallbacks(ECMC_ASYN_DEFAULT_LIST, ECMC_ASYN_DEFAULT_ADDR);
// Add resultdata "plugin.can.dev%d.error"
paramName = ECMC_PLUGIN_ASYN_PREFIX + std::string(".dev") +
to_string(nodeId_) + ".pdo" + "." + std::string(name_) + std::string(".error");
errorParam_ = ecmcAsynPort->addNewAvailParam(
paramName.c_str(), // name
asynParamInt32, // asyn type
(uint8_t*)&errorCode_, // pointer to data
sizeof(errorCode_), // size of data
ECMC_EC_U32, // ecmc data type
0); // die if fail
if(!errorParam_) {
printf("ERROR: Failed create asyn param for data.");
throw std::runtime_error( "ERROR: Failed create asyn param for data: " + paramName);
}
errorParam_->setAllowWriteToEcmc(false); // need to callback here
errorParam_->refreshParam(1); // read once into asyn param lib
ecmcAsynPort->callParamCallbacks(ECMC_ASYN_DEFAULT_LIST, ECMC_ASYN_DEFAULT_ADDR);
}
void ecmcCANOpenPDO::refreshAsynParams() {
if(refreshNeeded_) {
dataParam_->refreshParamRT(1); // read once into asyn param lib
errorParam_->refreshParamRT(1); // read once into asyn param lib
}
refreshNeeded_ = 0;
}
// Avoid issues with std:to_string()
std::string ecmcCANOpenPDO::to_string(int value) {
std::ostringstream os;
os << value;
return os.str();
}

View File

@@ -20,6 +20,7 @@
#include "inttypes.h"
#include <string>
#include "ecmcSocketCANWriteBuffer.h"
#include "epicsMutex.h"
#include <linux/can.h>
#include <linux/can/raw.h>
@@ -29,12 +30,14 @@
class ecmcCANOpenPDO {
public:
ecmcCANOpenPDO(ecmcSocketCANWriteBuffer* writeBuffer,
uint32_t nodeId,
uint32_t cobId,
ecmc_can_direction rw,
uint32_t ODSize,
int readTimeoutMs,
int writeCycleMs, //if <0 the write on demand..
int exeSampleTimeMs,
const char *name,
int dbgMode);
~ecmcCANOpenPDO();
void execute();
@@ -46,6 +49,7 @@ class ecmcCANOpenPDO {
int validateFrame(can_frame *frame);
ecmcSocketCANWriteBuffer *writeBuffer_;
uint32_t cobId_; // with cobid
uint32_t nodeId_;
int readTimeoutMs_;
int writeCycleMs_;
int exeSampleTimeMs_;
@@ -58,6 +62,17 @@ class ecmcCANOpenPDO {
void printBuffer();
int dbgMode_;
can_frame writeFrame_;
epicsMutexId dataMutex_;
char* name_;
static std::string to_string(int value);
//ASYN
void initAsyn();
void refreshAsynParams();
int refreshNeeded_;
ecmcAsynDataItem *dataParam_;
ecmcAsynDataItem *errorParam_;
};
#endif /* ECMC_CANOPEN_PDO_H_ */

View File

@@ -7,7 +7,6 @@
*
* Created on: Mar 22, 2020
* Author: anderssandstrom
* Credits to https://github.com/sgreg/dynamic-loading
*
\*************************************************************************/
@@ -16,13 +15,30 @@
#include <sstream>
#include "ecmcCANOpenSDO.h"
#include "ecmcAsynPortDriver.h"
#include "ecmcPluginClient.h"
#define ECMC_SDO_TRANSFER_MAX_BYTES 7
// Calback function to init write from asyn
asynStatus asynWriteSDOValue(void* data, size_t bytes, asynParamType asynParType,void *userObj) {
// userobj = NULL
if(!userObj) {
printf("Error: asynWriteSDOValue() fail, no user obj defined.\n");
return asynError;
}
ecmcCANOpenSDO* sdo = (ecmcCANOpenSDO*)userObj;
sdo->setValue((uint8_t*)data,bytes);
sdo->writeValue();
return asynSuccess;
}
/**
* ecmc ecmcCANOpenSDO class
*/
ecmcCANOpenSDO::ecmcCANOpenSDO(ecmcSocketCANWriteBuffer* writeBuffer,
uint32_t nodeId,
uint32_t cobIdTx, // 0x580 + CobId
uint32_t cobIdRx, // 0x600 + Cobid
ecmc_can_direction rw,
@@ -31,15 +47,29 @@ ecmcCANOpenSDO::ecmcCANOpenSDO(ecmcSocketCANWriteBuffer* writeBuffer,
uint32_t ODSize,
int readSampleTimeMs,
int exeSampleTimeMs,
const char *name,
std::atomic_flag *ptrSdo1Lock,
int objIndex,
int dbgMode) {
writeBuffer_ = writeBuffer;
nodeId_ = nodeId;
cobIdRx_ = cobIdRx;
cobIdTx_ = cobIdTx;
ODIndex_ = ODIndex;
ODSubIndex_ = ODSubIndex;
ODSize_ = ODSize;
objIndex_ = objIndex;
dbgMode_ = dbgMode;
name_ = strdup(name);
errorCode_ = 0;
refreshNeeded_ = 0;
ptrSdo1Lock_ = ptrSdo1Lock;
dataMutex_ = epicsMutexCreate();
getLockMutex_ = epicsMutexCreate();
errorParam_ = NULL;
dataParam_ = NULL;
writePending_ = NULL;
// convert to ODIndex_ to indiviual bytes struct
memcpy(&ODIndexBytes_, &ODIndex, 2);
memcpy(&ODLengthBytes_, &ODSize_, 4);
@@ -48,14 +78,18 @@ ecmcCANOpenSDO::ecmcCANOpenSDO(ecmcSocketCANWriteBuffer* writeBuffer,
exeSampleTimeMs_ = exeSampleTimeMs;
rw_ = rw;
exeCounter_ = 0;
busy_ = 0;
recivedBytes_ = 0;
writtenBytes_ = 0;
readStates_ = READ_IDLE;
writeStates_ = WRITE_IDLE;
useTg1Frame_ = 1;
dataBuffer_ = new uint8_t(ODSize_);
dataBuffer_ = new uint8_t[ODSize_];
tempDataBuffer_ = new uint8_t[ODSize_];
memset(dataBuffer_,0,ODSize_);
memset(tempDataBuffer_,0,ODSize_);
busyCounter_ = 0;
// Request data (send on slave RX)
// w 0x603 [8] 0x40 0x40 0x26 0x00 0x00 0x00 0x00 0x00
readReqTransferFrame_.can_id = cobIdRx;
@@ -165,36 +199,70 @@ ecmcCANOpenSDO::ecmcCANOpenSDO(ecmcSocketCANWriteBuffer* writeBuffer,
writeConfReqFrameTg1_.data[4] = 0;
writeConfReqFrameTg1_.data[5] = 0;
writeConfReqFrameTg1_.data[6] = 0;
writeConfReqFrameTg1_.data[7] = 0;
writeConfReqFrameTg1_.data[7] = 0;
busy_ = false;
initAsyn();
}
ecmcCANOpenSDO::~ecmcCANOpenSDO() {
delete[] dataBuffer_;
delete[] tempDataBuffer_;
free(name_);
}
void ecmcCANOpenSDO::execute() {
if(busy_) {
busyCounter_++;
} else {
busyCounter_ = 0;
}
if(busyCounter_>ECMC_SDO_REPLY_TIMOUT_MS) {
// cancel read or write
printf("Error: SDO BUSY timeout!! %s\n",name_);
memset(tempDataBuffer_,0,ODSize_);
readStates_ = READ_IDLE;
writeStates_ = WRITE_IDLE;
exeCounter_ = 0;
busyCounter_ = 0;
errorCode_ = ECMC_CAN_ERROR_SDO_TIMEOUT;
errorParam_->refreshParamRT(1);
unlockSdo1();
}
if(!busy_ && writePending_) {
// Try to write pending value in tempDataBuffer_
writeValue();
}
exeCounter_++;
if(exeCounter_* exeSampleTimeMs_ >= readSampleTimeMs_ && ! busy_) {
exeCounter_ =0;
if(rw_ == DIR_READ) {
busy_ = 1;
if(exeCounter_* exeSampleTimeMs_ < readSampleTimeMs_ && rw_ == DIR_READ) { // do not risk overflow
exeCounter_++;
} else { // Counter is higher, try to write
if(rw_ == DIR_READ) {
if(!tryLockSdo1()) {
// wait for busy to go down
return;
}
exeCounter_ =0;
readStates_ = READ_REQ_TRANSFER;
if(dbgMode_) {
printf("STATE = READ_REQ_TRANSFER\n");
printf("STATE = READ_REQ_TRANSFER %s\n",name_);
}
// IMPORTANT!! LOCKLOCK!!!! LOCK all slave trafic while 0x583 and 0x603 for any other trafic while processing
//initiate
recivedBytes_ = 0;
readStates_ = READ_WAIT_FOR_CONF;
writeBuffer_->addWriteCAN(&readReqTransferFrame_);
if(dbgMode_) {
printf("STATE = READ_WAIT_FOR_CONF\n");
printf("STATE = READ_WAIT_FOR_CONF %s\n",name_);
}
}
}
}
// Refresh in sync with ecmc
refreshAsynParams();
}
// new rx frame recived!
@@ -202,7 +270,7 @@ void ecmcCANOpenSDO::newRxFrame(can_frame *frame) {
// Wait for:
// # r 0x583 [8] 0x41 0x40 0x26 0x00 0x38 0x00 0x00 0x00
int errorCode = 0;
if(!busy_) {
if(!busy_) {
// Not waiting for any data..
return;
}
@@ -218,6 +286,10 @@ void ecmcCANOpenSDO::newRxFrame(can_frame *frame) {
else { // Write
errorCode = writeDataStateMachine(frame);
}
if(errorCode && (errorCode_ != errorCode)) {
errorCode_ = errorCode;
refreshNeeded_ = 1;
}
}
int ecmcCANOpenSDO::readDataStateMachine(can_frame *frame) {
@@ -230,20 +302,20 @@ int ecmcCANOpenSDO::readDataStateMachine(can_frame *frame) {
}
readStates_ = READ_WAIT_FOR_DATA; //Next frame should be data!
if(dbgMode_) {
printf("STATE = READ_WAIT_FOR_DATA\n");
printf("STATE = READ_WAIT_FOR_DATA %s\n",name_);
}
writeBuffer_->addWriteCAN(&readConfReqFrameTg0_); // Send tg0 frame and wait for data, also size must match to go ahead
useTg1Frame_ = 1;
break;
case READ_WAIT_FOR_DATA:
//Add data to buffer
bytesToRead = frame->can_dlc-1;
bytesToRead = frame->can_dlc - 1;
if( bytesToRead > ECMC_SDO_TRANSFER_MAX_BYTES) {
bytesToRead = ECMC_SDO_TRANSFER_MAX_BYTES;
}
if(bytesToRead + recivedBytes_ <= ODSize_) {
memcpy(dataBuffer_ + recivedBytes_, &(frame->data[1]),bytesToRead);
memcpy(tempDataBuffer_ + recivedBytes_, &(frame->data[1]),bytesToRead);
recivedBytes_ += bytesToRead;
}
if(recivedBytes_ < ODSize_) { // Ask for more data but must toggle so alternat the prepared frames
@@ -260,14 +332,20 @@ int ecmcCANOpenSDO::readDataStateMachine(can_frame *frame) {
}
if (recivedBytes_ >= ODSize_) {
readStates_ =READ_IDLE;
busy_ = 0;
readStates_ =READ_IDLE;
useTg1Frame_ = 0;
epicsMutexLock(dataMutex_);
memcpy(dataBuffer_,tempDataBuffer_,ODSize_);
epicsMutexUnlock(dataMutex_);
if(dbgMode_) {
printf("STATE = READ_IDLE\n");
printf("STATE = READ_IDLE %s\n",name_);
printf("All data read from slave SDO.\n");
printBuffer();
//copy complete data to dataBuffer_
printBuffer();
}
refreshNeeded_ = 1;
unlockSdo1();
return 0;
}
break;
@@ -279,7 +357,7 @@ int ecmcCANOpenSDO::readDataStateMachine(can_frame *frame) {
}
int ecmcCANOpenSDO::writeDataStateMachine(can_frame *frame) {
printf("writeDataStateMachine\n");
//printf("writeDataStateMachine %s\n",name_);
int bytes = 0;
switch(writeStates_) {
case WRITE_WAIT_FOR_CONF:
@@ -294,7 +372,7 @@ int ecmcCANOpenSDO::writeDataStateMachine(can_frame *frame) {
writeNextDataToSlave(useTg1Frame_);
writeStates_ = WRITE_DATA; //Next frame should be data!
if(dbgMode_) {
printf("STATE = WRITE_DATA\n");
printf("STATE = WRITE_DATA %s\n",name_);
}
break;
@@ -309,13 +387,13 @@ int ecmcCANOpenSDO::writeDataStateMachine(can_frame *frame) {
// Check if write was done already or if more frames are needed!
if(writtenBytes_ >= ODSize_) {
writeStates_ = WRITE_IDLE;
useTg1Frame_ = 0;
busy_ = 0;
useTg1Frame_ = 0;
if(dbgMode_) {
printf("STATE = WRITE_IDLE\n");
printf("STATE = WRITE_IDLE %s\n",name_);
printf("All data written to slave SDO.\n");
printBuffer();
}
unlockSdo1();
return 0;
}
@@ -412,49 +490,164 @@ void ecmcCANOpenSDO::setValue(uint8_t *data, size_t bytes) {
if(bytesToCopy == 0) {
return;
}
memcpy(dataBuffer_, &data, ODSize_);
// always write to tempDatabuffer then transfer
epicsMutexLock(dataMutex_);
memcpy(tempDataBuffer_, data, ODSize_);
epicsMutexUnlock(dataMutex_);
}
int ecmcCANOpenSDO::writeValue() {
// Busy right now!
printf("WRITEVALUE!!\n");
if(busy_ || writeStates_ != WRITE_IDLE ) {
//printf("WRITEVALUE %s\n",name_);
if(busy_) {
writePending_ = true;
return ECMC_CAN_ERROR_SDO_WRITE_BUSY;
}
busy_ = 1;
if(!tryLockSdo1()) {
// wait for busy to go down
writePending_ = true;
return ECMC_CAN_ERROR_SDO_WRITE_BUSY;
}
if(writeStates_ != WRITE_IDLE ) {
writePending_ = true;
return ECMC_CAN_ERROR_SDO_WRITE_BUSY;
}
writePending_ = false;
epicsMutexLock(dataMutex_);
memcpy(dataBuffer_, tempDataBuffer_, ODSize_);
epicsMutexUnlock(dataMutex_);
writeStates_ = WRITE_REQ_TRANSFER;
if(dbgMode_) {
printf("STATE = WRITE_REQ_TRANSFER\n");
printf("STATE = WRITE_REQ_TRANSFER %s\n",name_);
}
writeBuffer_->addWriteCAN(&writeReqTransferFrame_);
writeStates_ = WRITE_WAIT_FOR_CONF;
if(dbgMode_) {
printf("STATE = WRITE_WAIT_FOR_CONF\n");
printf("STATE = WRITE_WAIT_FOR_CONF %s\n",name_);
}
return 0;
// State machine is now in rx frame()
}
//# w 0x603 [8] 0x40 0x40 0x26 0x00 0x00 0x00 0x00 0x00
//# w 0x603 [8] 0x61 0x40 0x26 0x00 0x00 0x00 0x00 0x00
//# w 0x603 [8] 0x71 0x40 0x26 0x00 0x00 0x00 0x00 0x00
//# w 0x603 [8] 0x61 0x40 0x26 0x00 0x00 0x00 0x00 0x00
//# w 0x603 [8] 0x71 0x40 0x26 0x00 0x00 0x00 0x00 0x00
//# w 0x603 [8] 0x61 0x40 0x26 0x00 0x00 0x00 0x00 0x00
//# w 0x603 [8] 0x71 0x40 0x26 0x00 0x00 0x00 0x00 0x00
//# w 0x603 [8] 0x61 0x40 0x26 0x00 0x00 0x00 0x00 0x00
//#
//# 0x38 bytes to recive!!
//# r 0x583 [8] 0x41 0x40 0x26 0x00 0x38 0x00 0x00 0x00
//# Data below
//# r 0x583 [8] 0x00 0x18 0x00 0x24 0x00 0x0E 0x00 0x0A
//# r 0x583 [8] 0x10 0x00 0x00 0x00 0xBF 0x00 0x00 0x00
//# r 0x583 [8] 0x00 0xC2 0x01 0x00 0x00 0x00 0x00 0x35
//# r 0x583 [8] 0x10 0x1C 0x84 0x02 0x46 0x1A 0x3C 0x49
//# r 0x583 [8] 0x00 0xC2 0x01 0x00 0x00 0x00 0x00 0x00
//# r 0x583 [8] 0x10 0x00 0xC8 0x48 0x51 0x2F 0x00 0x00
//# r 0x583 [8] 0x00 0x5C 0x2D 0x81 0x14 0x67 0x0D 0xA6
//
int ecmcCANOpenSDO::tryLockSdo1() {
epicsMutexLock(getLockMutex_);
if(busy_) {
epicsMutexUnlock(getLockMutex_);
return 0;
}
bool prevLock = ptrSdo1Lock_->test_and_set();
if(prevLock) {
// wait for busy to go down
epicsMutexUnlock(getLockMutex_);
return 0;
}
busy_ = true;
epicsMutexUnlock(getLockMutex_);
return 1;
}
int ecmcCANOpenSDO::unlockSdo1() {
epicsMutexLock(getLockMutex_);
if(busy_) {
ptrSdo1Lock_->clear();
busy_ = false;
}
epicsMutexUnlock(getLockMutex_);
return 0;
}
void ecmcCANOpenSDO::initAsyn() {
ecmcAsynPortDriver *ecmcAsynPort = (ecmcAsynPortDriver *)getEcmcAsynPortDriver();
if(!ecmcAsynPort) {
printf("ERROR: ecmcAsynPort NULL.");
throw std::runtime_error( "ERROR: ecmcAsynPort NULL." );
}
// Add resultdata "plugin.can.dev%d.<name>"
std::string paramName = ECMC_PLUGIN_ASYN_PREFIX + std::string(".dev") +
to_string(nodeId_) + ".sdo" /*+ to_string(objIndex_) */
+ "." + std::string(name_);
dataParam_ = ecmcAsynPort->addNewAvailParam(
paramName.c_str(), // name
asynParamInt8Array, // asyn type
dataBuffer_, // pointer to data
ODSize_, // size of data
ECMC_EC_U8, // ecmc data type
0); // die if fail
if(!dataParam_) {
printf("ERROR: Failed create asyn param for data.");
throw std::runtime_error( "ERROR: Failed create asyn param for data: " + paramName);
}
// Allow different types depending on size
if(ODSize_>1){
dataParam_->addSupportedAsynType(asynParamInt16Array);
}
if(ODSize_>3){
dataParam_->addSupportedAsynType(asynParamInt32Array);
dataParam_->addSupportedAsynType(asynParamFloat32Array);
dataParam_->addSupportedAsynType(asynParamInt32);
}
if(ODSize_>7){
dataParam_->addSupportedAsynType(asynParamFloat64Array);
dataParam_->addSupportedAsynType(asynParamFloat64);
}
dataParam_->setAllowWriteToEcmc(rw_ == DIR_WRITE);
if(rw_ == DIR_WRITE) {
dataParam_->setExeCmdFunctPtr(asynWriteSDOValue,this);
}
dataParam_->refreshParam(1); // read once into asyn param lib
ecmcAsynPort->callParamCallbacks(ECMC_ASYN_DEFAULT_LIST, ECMC_ASYN_DEFAULT_ADDR);
// Add resultdata "plugin.can.dev%d.error"
paramName = ECMC_PLUGIN_ASYN_PREFIX + std::string(".dev") +
to_string(nodeId_) + ".sdo" + "." + std::string(name_) + std::string(".error");
errorParam_ = ecmcAsynPort->addNewAvailParam(
paramName.c_str(), // name
asynParamInt32, // asyn type
(uint8_t*)&errorCode_, // pointer to data
sizeof(errorCode_), // size of data
ECMC_EC_U32, // ecmc data type
0); // die if fail
if(!errorParam_) {
printf("ERROR: Failed create asyn param for data.");
throw std::runtime_error( "ERROR: Failed create asyn param for data: " + paramName);
}
errorParam_->setAllowWriteToEcmc(false); // need to callback here
errorParam_->refreshParam(1); // read once into asyn param lib
ecmcAsynPort->callParamCallbacks(ECMC_ASYN_DEFAULT_LIST, ECMC_ASYN_DEFAULT_ADDR);
}
void ecmcCANOpenSDO::refreshAsynParams() {
if(refreshNeeded_) {
dataParam_->refreshParamRT(1); // read once into asyn param lib
errorParam_->refreshParamRT(1); // read once into asyn param lib
}
refreshNeeded_ = 0;
}
// Avoid issues with std:to_string()
std::string ecmcCANOpenSDO::to_string(int value) {
std::ostringstream os;
os << value;
return os.str();
}

View File

@@ -20,25 +20,31 @@
#include "inttypes.h"
#include <string>
#include "ecmcSocketCANWriteBuffer.h"
#include "ecmcDataItem.h"
#include <linux/can.h>
#include <linux/can/raw.h>
#define ECMC_CAN_ERROR_SDO_WRITE_BUSY 110
#define ECMC_CAN_ERROR_SDO_TIMEOUT 111
class ecmcCANOpenSDO {
public:
ecmcCANOpenSDO(ecmcSocketCANWriteBuffer* writeBuffer,
uint32_t nodeId,
uint32_t cobIdTx, // 0x580 + CobId
uint32_t cobIdRx, // 0x600 + Cobid
ecmc_can_direction rw,
uint16_t ODIndex, // Object dictionary index
uint8_t ODSubIndex, // Object dictionary subindex
uint32_t ODSize,
int readSampleTimeMs,
int exeSampleTimeMs,
int readSampleTimeMs,
int exeSampleTimeMs,
const char *name,
std::atomic_flag *ptrSdo1Lock,
int objIndex,
int dbgMode);
~ecmcCANOpenSDO();
void execute();
@@ -52,8 +58,11 @@ class ecmcCANOpenSDO {
int writeDataStateMachine(can_frame *frame);
int writeNextDataToSlave(int useToggle);
int writeWaitForDataConfFrame(int useToggle, can_frame *frame);
int tryLockSdo1();
int unlockSdo1();
ecmcSocketCANWriteBuffer *writeBuffer_;
uint32_t nodeId_; // with cobid
uint32_t cobIdRx_; // with cobid
uint32_t cobIdTx_; // with cobid
int readSampleTimeMs_;
@@ -77,14 +86,35 @@ class ecmcCANOpenSDO {
can_frame writeConfReqFrameTg1_;
int dbgMode_;
int busy_;
int errorCode_;
int objIndex_;
uint8_t *dataBuffer_;
uint8_t *tempDataBuffer_;
uint32_t recivedBytes_;
int useTg1Frame_;
ecmc_read_states readStates_;
ecmc_write_states writeStates_;
void printBuffer();
uint32_t writtenBytes_;
char *name_;
epicsMutexId dataMutex_;
epicsMutexId getLockMutex_;
int busyCounter_;
std::atomic_flag *ptrSdo1Lock_;
bool busy_;
bool writePending_;
static std::string to_string(int value);
//ASYN
void initAsyn();
void refreshAsynParams();
int refreshNeeded_;
ecmcAsynDataItem *dataParam_;
ecmcAsynDataItem *errorParam_;
};
#endif /* ECMC_CANOPEN_SDO_H_ */

View File

@@ -7,6 +7,7 @@
*
* Created on: Mar 21, 2020
* Author: anderssandstrom
* Credits to https://github.com/sgreg/dynamic-loading
*
\*************************************************************************/

484
src/ecmcSocketCAN.cpp Normal file
View File

@@ -0,0 +1,484 @@
/*************************************************************************\
* Copyright (c) 2019 European Spallation Source ERIC
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcSocketCAN.cpp
*
* Created on: Mar 22, 2020
* Author: anderssandstrom
* Credits to https://github.com/sgreg/dynamic-loading
*
\*************************************************************************/
// Needed to get headers in ecmc right...
#define ECMC_IS_PLUGIN
#include <sstream>
#include "ecmcSocketCAN.h"
#include "ecmcPluginClient.h"
#include "ecmcAsynPortDriver.h"
#include "ecmcAsynPortDriverUtils.h"
#include "epicsThread.h"
// Start worker for socket read()
void f_worker_read(void *obj) {
if(!obj) {
printf("%s/%s:%d: Error: Worker read thread ecmcSocketCAN object NULL..\n",
__FILE__, __FUNCTION__, __LINE__);
return;
}
ecmcSocketCAN * canObj = (ecmcSocketCAN*)obj;
canObj->doReadWorker();
}
// Start worker for socket connect()
void f_worker_connect(void *obj) {
if(!obj) {
printf("%s/%s:%d: Error: Worker connect thread ecmcSocketCAN object NULL..\n",
__FILE__, __FUNCTION__, __LINE__);
return;
}
ecmcSocketCAN * canObj = (ecmcSocketCAN*)obj;
canObj->doConnectWorker();
}
/** ecmc ecmcSocketCAN class
* This object can throw:
* - bad_alloc
* - invalid_argument
* - runtime_error
*/
ecmcSocketCAN::ecmcSocketCAN(char* configStr,
char* portName,
int exeSampleTimeMs) {
// Init
cfgCanIFStr_ = NULL;
cfgDbgMode_ = 0;
cfgAutoConnect_ = 1;
destructs_ = 0;
socketId_ = -1;
connected_ = 0;
writeBuffer_ = NULL;
deviceCounter_ = 0;
refreshNeeded_ = 0;
errorCode_ = 0;
masterDev_ = NULL;
for(int i = 0; i<ECMC_CAN_MAX_DEVICES;i++) {
devices_[i] = NULL;
}
exeSampleTimeMs_ = exeSampleTimeMs;
memset(&ifr_,0,sizeof(struct ifreq));
memset(&rxmsg_,0,sizeof(struct can_frame));
memset(&addr_,0,sizeof(struct sockaddr_can));
parseConfigStr(configStr); // Assigns all configs
// Check valid nfft
if(!cfgCanIFStr_ ) {
throw std::out_of_range("CAN inteface must be defined (can0, vcan0...).");
}
// Create worker thread for reading socket
std::string threadname = "ecmc." ECMC_PLUGIN_ASYN_PREFIX".read";
if(epicsThreadCreate(threadname.c_str(), 0, 32768, f_worker_read, this) == NULL) {
throw std::runtime_error("Error: Failed create worker thread for read().");
}
// Create worker thread for connecting socket
threadname = "ecmc." ECMC_PLUGIN_ASYN_PREFIX".connect";
if(epicsThreadCreate(threadname.c_str(), 0, 32768, f_worker_connect, this) == NULL) {
throw std::runtime_error("Error: Failed create worker thread for connect().");
}
if(cfgAutoConnect_) {
connectPrivate();
}
writeBuffer_ = new ecmcSocketCANWriteBuffer(socketId_, cfgDbgMode_);
initAsyn();
}
ecmcSocketCAN::~ecmcSocketCAN() {
// kill worker
destructs_ = 1; // maybe need todo in other way..
doWriteEvent_.signal();
doConnectEvent_.signal();
for(int i = 0; i<ECMC_CAN_MAX_DEVICES;i++) {
delete devices_[i];
}
}
void ecmcSocketCAN::parseConfigStr(char *configStr) {
// check config parameters
if (configStr && configStr[0]) {
char *pOptions = strdup(configStr);
char *pThisOption = pOptions;
char *pNextOption = pOptions;
while (pNextOption && pNextOption[0]) {
pNextOption = strchr(pNextOption, ';');
if (pNextOption) {
*pNextOption = '\0'; /* Terminate */
pNextOption++; /* Jump to (possible) next */
}
// ECMC_PLUGIN_DBG_PRINT_OPTION_CMD (1/0)
if (!strncmp(pThisOption, ECMC_PLUGIN_DBG_PRINT_OPTION_CMD, strlen(ECMC_PLUGIN_DBG_PRINT_OPTION_CMD))) {
pThisOption += strlen(ECMC_PLUGIN_DBG_PRINT_OPTION_CMD);
cfgDbgMode_ = atoi(pThisOption);
}
// ECMC_PLUGIN_CONNECT_OPTION_CMD (1/0)
if (!strncmp(pThisOption, ECMC_PLUGIN_CONNECT_OPTION_CMD, strlen(ECMC_PLUGIN_CONNECT_OPTION_CMD))) {
pThisOption += strlen(ECMC_PLUGIN_DBG_PRINT_OPTION_CMD);
cfgAutoConnect_ = atoi(pThisOption);
}
// ECMC_PLUGIN_IF_OPTION_CMD (Source string)
else if (!strncmp(pThisOption, ECMC_PLUGIN_IF_OPTION_CMD, strlen(ECMC_PLUGIN_IF_OPTION_CMD))) {
pThisOption += strlen(ECMC_PLUGIN_IF_OPTION_CMD);
cfgCanIFStr_=strdup(pThisOption);
}
pThisOption = pNextOption;
}
free(pOptions);
}
if(!cfgCanIFStr_) {
throw std::invalid_argument( "CAN interface not defined.");
}
}
// For connect commands over asyn or plc. let worker connect
void ecmcSocketCAN::connectExternal() {
if(!connected_) {
doConnectEvent_.signal(); // let worker start
}
}
void ecmcSocketCAN::connectPrivate() {
if((socketId_ = socket(PF_CAN, SOCK_RAW, CAN_RAW)) == -1) {
throw std::runtime_error( "Error while opening socket.");
return;
}
strcpy(ifr_.ifr_name, cfgCanIFStr_);
ioctl(socketId_, SIOCGIFINDEX, &ifr_);
addr_.can_family = AF_CAN;
addr_.can_ifindex = ifr_.ifr_ifindex;
printf("%s at index %d\n", cfgCanIFStr_, ifr_.ifr_ifindex);
if(bind(socketId_, (struct sockaddr *)&addr_, sizeof(addr_)) == -1) {
throw std::runtime_error( "Error in socket bind.");
return;
}
connected_ = 1;
}
int ecmcSocketCAN::getConnected() {
return connected_;
}
// Read socket worker
void ecmcSocketCAN::doReadWorker() {
while(true) {
if(destructs_) {
break;
}
if(!connected_) {
timespec tempPauseTime;
tempPauseTime.tv_sec = 1;
tempPauseTime.tv_nsec = 0;
nanosleep(&tempPauseTime,NULL);
continue;
}
// Wait for new CAN frame
int bytes = read(socketId_, &rxmsg_, sizeof(rxmsg_));
// error in read()
if(bytes == -1) {
errorCode_ = errno;
printf("ecmcSocketCAN: read() fail with error: %s, (0x%x).\n", strerror(errno),errno);
refreshNeeded_ = 1;
continue;
}
// incomplete read()
if(bytes != sizeof(rxmsg_)) {
printf("ecmcSocketCAN: read() fail with error: Incomplete read, not a full can frame (0x%x).\n",ECMC_CAN_ERROR_READ_INCOMPLETE);
errorCode_ = ECMC_CAN_ERROR_READ_INCOMPLETE;
refreshNeeded_ = 1;
continue;
}
// forward all data to devices (including master)
for(int i = 0; i < deviceCounter_; i++){
devices_[i]->newRxFrame(&rxmsg_);
}
if(cfgDbgMode_) {
// Simulate candump printout
printf("r 0x%03X", rxmsg_.can_id);
printf(" [%d]", rxmsg_.can_dlc);
for(int i=0; i<rxmsg_.can_dlc; i++ ) {
printf(" 0x%02X", rxmsg_.data[i]);
}
printf("\n");
}
}
}
// Connect socket worker
void ecmcSocketCAN::doConnectWorker() {
while(true) {
if(destructs_) {
return;
}
doConnectEvent_.wait();
if(destructs_) {
return;
}
connectPrivate();
}
}
int ecmcSocketCAN::getlastWritesError() {
if(!writeBuffer_) {
return ECMC_CAN_ERROR_WRITE_BUFFER_NULL;
}
return writeBuffer_->getlastWritesErrorAndReset();
}
int ecmcSocketCAN::addWriteCAN(uint32_t canId,
uint8_t len,
uint8_t data0,
uint8_t data1,
uint8_t data2,
uint8_t data3,
uint8_t data4,
uint8_t data5,
uint8_t data6,
uint8_t data7) {
if(!writeBuffer_) {
return ECMC_CAN_ERROR_WRITE_BUFFER_NULL;
}
writeBuffer_->addWriteCAN(canId,
len,
data0,
data1,
data2,
data3,
data4,
data5,
data6,
data7);
return 0;
}
void ecmcSocketCAN::execute() {
for(int i = 0; i < deviceCounter_; i++){
devices_[i]->execute();
}
int writeError=getlastWritesError();
if (writeError) {
errorCode_ = writeError;
refreshNeeded_ = 1;
}
refreshAsynParams();
return;
}
// Avoid issues with std:to_string()
std::string ecmcSocketCAN::to_string(int value) {
std::ostringstream os;
os << value;
return os.str();
}
void ecmcSocketCAN::addMaster(uint32_t nodeId,
const char* name,
int lssSampleTimeMs,
int syncSampleTimeMs,
int heartSampleTimeMs) {
if(masterDev_) {
throw std::runtime_error("Master already added.");
}
if(deviceCounter_ >= ECMC_CAN_MAX_DEVICES) {
throw std::out_of_range("Device array full.");
}
if(nodeId >= 128) {
throw std::out_of_range("Node id out of range.");
}
if(lssSampleTimeMs <= 0) {
throw std::out_of_range("LSS sample time ms out of range.");
}
if(syncSampleTimeMs <= 0) {
throw std::out_of_range("Sync sample time ms out of range.");
}
if(heartSampleTimeMs <= 0) {
throw std::out_of_range("Heart sample time ms out of range.");
}
masterDev_ = new ecmcCANOpenMaster(writeBuffer_,
nodeId,
exeSampleTimeMs_,
lssSampleTimeMs,
syncSampleTimeMs,
heartSampleTimeMs,
name,
cfgDbgMode_);
// add as a normal device also for execute and rxframe
devices_[deviceCounter_] = masterDev_;
deviceCounter_++;
}
void ecmcSocketCAN::addDevice(uint32_t nodeId,
const char* name,
int heartTimeoutMs){
if(deviceCounter_ >= ECMC_CAN_MAX_DEVICES) {
throw std::out_of_range("Device array full.");
}
if(nodeId >= 128) {
throw std::out_of_range("Node id out of range.");
}
devices_[deviceCounter_] = new ecmcCANOpenDevice(writeBuffer_,nodeId,exeSampleTimeMs_,name,heartTimeoutMs,cfgDbgMode_);
deviceCounter_++;
}
int ecmcSocketCAN::findDeviceWithNodeId(uint32_t nodeId) {
for(int i=0; i < deviceCounter_;i++) {
if(devices_[i]) {
if(devices_[i]->getNodeId() == nodeId) {
return i;
}
}
}
return -1;
}
void ecmcSocketCAN::addPDO(uint32_t nodeId,
uint32_t cobId,
ecmc_can_direction rw,
uint32_t ODSize,
int readTimeoutMs,
int writeCycleMs, //if <0 then write on demand.
const char* name) {
int devId = findDeviceWithNodeId(nodeId);
if(devId < 0) {
throw std::out_of_range("Node id not found in any configured device.");
}
int errorCode = devices_[devId]->addPDO(cobId,
rw,
ODSize,
readTimeoutMs,
writeCycleMs,
name);
if(errorCode > 0) {
throw std::runtime_error("AddPDO() failed.");
}
}
void ecmcSocketCAN::addSDO(uint32_t nodeId,
uint32_t cobIdTx, // 0x580 + CobId
uint32_t cobIdRx, // 0x600 + Cobid
ecmc_can_direction rw,
uint16_t ODIndex, // Object dictionary index
uint8_t ODSubIndex, // Object dictionary subindex
uint32_t ODSize,
int readSampleTimeMs,
const char* name) {
int devId = findDeviceWithNodeId(nodeId);
if(devId < 0) {
throw std::out_of_range("Node id not found in any configured device.");
}
int errorCode = devices_[devId]->addSDO(cobIdTx,
cobIdRx,
rw,
ODIndex,
ODSubIndex,
ODSize,
readSampleTimeMs,
name);
if(errorCode > 0) {
throw std::runtime_error("AddSDO() failed.");
}
}
void ecmcSocketCAN::initAsyn() {
ecmcAsynPortDriver *ecmcAsynPort = (ecmcAsynPortDriver *)getEcmcAsynPortDriver();
if(!ecmcAsynPort) {
printf("ERROR: ecmcAsynPort NULL.");
throw std::runtime_error( "ERROR: ecmcAsynPort NULL." );
}
// Add resultdata "plugin.can.read.error"
std::string paramName = ECMC_PLUGIN_ASYN_PREFIX + std::string(".com.error");
errorParam_ = ecmcAsynPort->addNewAvailParam(
paramName.c_str(), // name
asynParamInt32, // asyn type
(uint8_t*)&errorCode_, // pointer to data
sizeof(errorCode_), // size of data
ECMC_EC_U32, // ecmc data type
0); // die if fail
if(!errorParam_) {
printf("ERROR: Failed create asyn param for data.");
throw std::runtime_error( "ERROR: Failed create asyn param for: " + paramName);
}
errorParam_->setAllowWriteToEcmc(false); // need to callback here
errorParam_->refreshParam(1); // read once into asyn param lib
ecmcAsynPort->callParamCallbacks(ECMC_ASYN_DEFAULT_LIST, ECMC_ASYN_DEFAULT_ADDR);
// Add resultdata "plugin.can.read.connected"
paramName = ECMC_PLUGIN_ASYN_PREFIX + std::string(".com.connected");
connectedParam_ = ecmcAsynPort->addNewAvailParam(
paramName.c_str(), // name
asynParamInt32, // asyn type
(uint8_t*)&connected_, // pointer to data
sizeof(connected_), // size of data
ECMC_EC_U32, // ecmc data type
0); // die if fail
if(!connectedParam_) {
printf("ERROR: Failed create asyn param for connected.");
throw std::runtime_error( "ERROR: Failed create asyn param for: " + paramName);
}
connectedParam_->setAllowWriteToEcmc(false); // need to callback here
connectedParam_->refreshParam(1); // read once into asyn param lib
ecmcAsynPort->callParamCallbacks(ECMC_ASYN_DEFAULT_LIST, ECMC_ASYN_DEFAULT_ADDR);
}
// only refresh from "execute" thread
void ecmcSocketCAN::refreshAsynParams() {
if(refreshNeeded_) {
connectedParam_->refreshParamRT(1); // read once into asyn param lib
errorParam_->refreshParamRT(1); // read once into asyn param lib
}
refreshNeeded_ = 0;
}

3
src/ecmcSocketCAN.dbd Normal file
View File

@@ -0,0 +1,3 @@
registrar("ecmcCANPluginDriverRegister")
function(ecmcByteToArrayInit)
function(ecmcByteToArray)

View File

@@ -17,8 +17,8 @@
#include "ecmcAsynPortDriver.h"
#include "ecmcSocketCANDefs.h"
#include "ecmcSocketCANWriteBuffer.h"
#include "ecmcCANOpenSDO.h"
#include "ecmcCANOpenPDO.h"
#include "ecmcCANOpenDevice.h"
#include "ecmcCANOpenMaster.h"
#include "inttypes.h"
#include <string>
@@ -36,13 +36,15 @@
#include <linux/can/raw.h>
#define ECMC_CAN_MAX_WRITE_CMDS 128
#define ECMC_CAN_MAX_DEVICES 128
#define ECMC_CAN_ERROR_WRITE_FULL 10
#define ECMC_CAN_ERROR_WRITE_BUSY 11
#define ECMC_CAN_ERROR_WRITE_NO_DATA 12
#define ECMC_CAN_ERROR_WRITE_INCOMPLETE 13
#define ECMC_CAN_ERROR_WRITE_BUFFER_NULL 14
#define ECMC_CAN_ERROR_READ_INCOMPLETE 15
class ecmcSocketCAN : public asynPortDriver {
class ecmcSocketCAN {
public:
/** ecmc ecmcSocketCAN class
@@ -61,11 +63,11 @@ class ecmcSocketCAN : public asynPortDriver {
void doWriteWorker();
void doConnectWorker();
virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
virtual asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
virtual asynStatus readInt8Array(asynUser *pasynUser, epicsInt8 *value,
size_t nElements, size_t *nIn);
virtual asynStatus readFloat64(asynUser *pasynUser, epicsFloat64 *value);
//virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
//virtual asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
//virtual asynStatus readInt8Array(asynUser *pasynUser, epicsInt8 *value,
// size_t nElements, size_t *nIn);
//virtual asynStatus readFloat64(asynUser *pasynUser, epicsFloat64 *value);
void connectExternal();
int getConnected();
int addWriteCAN(uint32_t canId,
@@ -80,10 +82,39 @@ class ecmcSocketCAN : public asynPortDriver {
uint8_t data7);
int getlastWritesError();
void execute(); // ecmc rt loop
void addMaster(uint32_t nodeId,
const char* name,
int lssSampleTimeMs,
int syncSampleTimeMs,
int heartSampleTimeMs);
void addDevice(uint32_t nodeId,
const char* name,
int heartTimeoutMs);
void addPDO(uint32_t nodeId,
uint32_t cobId,
ecmc_can_direction rw,
uint32_t ODSize,
int readTimeoutMs,
int writeCycleMs, //if <0 then write on demand.
const char* name);
void addSDO(uint32_t nodeId,
uint32_t cobIdTx, // 0x580 + CobId
uint32_t cobIdRx, // 0x600 + Cobid
ecmc_can_direction rw,
uint16_t ODIndex, // Object dictionary index
uint8_t ODSubIndex, // Object dictionary subindex
uint32_t ODSize,
int readSampleTimeMs,
const char* name);
int findDeviceWithNodeId(uint32_t nodeId);
private:
void parseConfigStr(char *configStr);
void initAsyn();
static std::string to_string(int value);
void connectPrivate();
int writeCAN(can_frame *frame);
@@ -99,19 +130,20 @@ class ecmcSocketCAN : public asynPortDriver {
int socketId_;
struct sockaddr_can addr_;
struct can_frame txmsgBuffer_[ECMC_CAN_MAX_WRITE_CMDS];
int writeCmdCounter_;
int writeBusy_;
int lastWriteSumError_;
int exeSampleTimeMs_;
ecmcSocketCANWriteBuffer *writeBuffer_;
ecmcCANOpenSDO *testSdo_;
ecmcCANOpenPDO *testPdo_;
ecmcCANOpenPDO *lssPdo_;
ecmcCANOpenPDO *syncPdo_;
ecmcCANOpenPDO *heartPdo_;
ecmcCANOpenSDO *basicConfSdo_;
int cycleCounter_;
int deviceCounter_;
ecmcCANOpenDevice *devices_[ECMC_CAN_MAX_DEVICES];
ecmcCANOpenMaster *masterDev_;
int errorCode_;
int refreshNeeded_;
//ASYN
void initAsyn();
void refreshAsynParams();
ecmcAsynDataItem *errorParam_;
ecmcAsynDataItem *connectedParam_;
};
#endif /* ECMC_SOCKETCAN_H_ */

View File

@@ -7,7 +7,6 @@
*
* Created on: March 02, 2021
* Author: anderssandstrom
* Credits to https://github.com/sgreg/dynamic-loading
*
\*************************************************************************/
@@ -19,15 +18,26 @@
#define ECMC_PLUGIN_IF_OPTION_CMD "IF="
#define ECMC_PLUGIN_CONNECT_OPTION_CMD "CONNECT="
#define ECMC_CANOPEN_NMT_BASE 0x700
#define ECMC_CANOPEN_NMT_BOOT 0x0
#define ECMC_CANOPEN_NMT_STOP 0x4
#define ECMC_CANOPEN_NMT_OP 0x5
#define ECMC_CANOPEN_NMT_PREOP 0x7F
#define ECMC_SDO_REPLY_TIMOUT_MS 1000
#define ECMC_PLUGIN_ASYN_PREFIX "plugin.can"
enum ecmc_can_direction {
DIR_READ,
DIR_WRITE };
DIR_WRITE = 1,
DIR_READ = 2 };
enum ecmc_read_states {
READ_IDLE,
READ_REQ_TRANSFER,
READ_WAIT_FOR_CONF,
READ_WAIT_FOR_DATA};
READ_WAIT_FOR_DATA
};
enum ecmc_write_states {
WRITE_IDLE,
@@ -36,6 +46,14 @@ enum ecmc_write_states {
WRITE_DATA,
};
enum ecmc_nmt_state_act {
NMT_NOT_VALID = 0,
NMT_BOOT_UP = 1,
NMT_STOPPED = 2,
NMT_OP = 3,
NMT_PREOP = 4
};
struct ODIndexBytes {
char byte0:8;
char byte1:8;

560
src/ecmcSocketCANWrap.cpp Normal file
View File

@@ -0,0 +1,560 @@
/*************************************************************************\
* Copyright (c) 2019 European Spallation Source ERIC
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcFFTWrap.cpp
*
* Created on: Mar 22, 2020
* Author: anderssandstrom
*
\*************************************************************************/
// Needed to get headers in ecmc right...
#define ECMC_IS_PLUGIN
#include <vector>
#include <stdexcept>
#include <string>
#include "ecmcSocketCANWrap.h"
#include "ecmcSocketCAN.h"
#include "ecmcSocketCANDefs.h"
#include <epicsTypes.h>
#include <epicsTime.h>
#include <epicsThread.h>
#include <epicsString.h>
#include <epicsTimer.h>
#include <epicsMutex.h>
#include <epicsExport.h>
#include <epicsEvent.h>
#include <iocsh.h>
#define ECMC_PLUGIN_MAX_PORTNAME_CHARS 64
#define ECMC_PLUGIN_PORTNAME_PREFIX "PLUGIN.CAN"
static ecmcSocketCAN* can = NULL;
static char portNameBuffer[ECMC_PLUGIN_MAX_PORTNAME_CHARS];
int createSocketCAN(char* configStr, int exeSampleTimeMs) {
// create new ecmcFFT object
// create asynport name for new object ()
memset(portNameBuffer, 0, ECMC_PLUGIN_MAX_PORTNAME_CHARS);
snprintf (portNameBuffer, ECMC_PLUGIN_MAX_PORTNAME_CHARS,
ECMC_PLUGIN_PORTNAME_PREFIX);
try {
can = new ecmcSocketCAN(configStr, portNameBuffer, exeSampleTimeMs);
}
catch(std::exception& e) {
if(can) {
delete can;
}
printf("Exception: %s. Plugin will unload.\n",e.what());
return ECMC_PLUGIN_SOCKETCAN_ERROR_CODE;
}
return 0;
}
int connectSocketCAN() {
if(can){
try {
can->connectExternal();
}
catch(std::exception& e) {
printf("Exception: %s.\n",e.what());
return ECMC_PLUGIN_SOCKETCAN_ERROR_CODE;
}
}
else {
return ECMC_PLUGIN_SOCKETCAN_ERROR_CODE;
}
return 0;
}
int getSocketCANConnectd() {
if(can){
try {
return can->getConnected();
}
catch(std::exception& e) {
printf("Exception: %s.\n",e.what());
return 0;
}
}
return 0;
}
int getlastWritesError() {
if(can){
try {
return can->getlastWritesError();
}
catch(std::exception& e) {
printf("Exception: %s.\n",e.what());
return 1;
}
}
return 1;
}
int execute() {
if(can){
can->execute();
}
return 0;
}
int addWriteSocketCAN( double canId,
double len,
double data0,
double data1,
double data2,
double data3,
double data4,
double data5,
double data6,
double data7) {
if(can){
try {
return can->addWriteCAN((uint32_t) canId,
(uint8_t) len,
(uint8_t) data0,
(uint8_t) data1,
(uint8_t) data2,
(uint8_t) data3,
(uint8_t) data4,
(uint8_t) data5,
(uint8_t) data6,
(uint8_t) data7);
}
catch(std::exception& e) {
printf("Exception: %s.\n",e.what());
return ECMC_PLUGIN_SOCKETCAN_ERROR_CODE;
}
}
return ECMC_PLUGIN_SOCKETCAN_ERROR_CODE;
}
void deleteSocketCAN() {
if(can) {
delete (can);
}
}
/**
* EPICS iocsh shell command: ecmcCANOpenAddMaster
*/
void ecmcCANOpenAddMasterPrintHelp() {
printf("\n");
printf(" Use ecmcCANOpenAddMaster(<name>, <node id>,....)\n");
printf(" <name> : Name of master device.\n");
printf(" <node id> : CANOpen node id of master.\n");
printf(" <LSS sample time ms> : Sample time for LSS.\n");
printf(" <Sync sample time ms> : Sample time for SYNC.\n");
printf(" <NMT Heartbeat sample time ms> : Sample time for NMT Heartbeat.\n");
printf("\n");
}
int ecmcCANOpenAddMaster(const char* name,
int nodeId,
int lssSampleTimeMs,
int syncSampleTimeMs,
int heartSampleTimeMs) {
if(!name) {
printf("Error: name.\n");
ecmcCANOpenAddMasterPrintHelp();
return asynError;
}
if(strcmp(name,"-h") == 0 || strcmp(name,"--help") == 0 ) {
ecmcCANOpenAddMasterPrintHelp();
return asynSuccess;
}
if(!can) {
printf("Plugin not initialized/loaded.\n");
return asynError;
}
try {
can->addMaster((uint32_t)nodeId,
name,
lssSampleTimeMs,
syncSampleTimeMs,
heartSampleTimeMs);
}
catch(std::exception& e) {
printf("Exception: %s. Add master failed.\n",e.what());
return asynError;
}
return asynSuccess;
}
static const iocshArg initArg0_0 =
{ "Name", iocshArgString };
static const iocshArg initArg1_0 =
{ "Node Id", iocshArgInt };
static const iocshArg initArg2_0 =
{ "LSS sample time ms", iocshArgInt };
static const iocshArg initArg3_0 =
{ "Sync sample time ms", iocshArgInt };
static const iocshArg initArg4_0 =
{ "NMT Heart sample time ms", iocshArgInt };
static const iocshArg *const initArgs_0[] = { &initArg0_0,
&initArg1_0,
&initArg2_0,
&initArg3_0,
&initArg4_0};
static const iocshFuncDef initFuncDef_0 = { "ecmcCANOpenAddMaster", 5, initArgs_0 };
static void initCallFunc_0(const iocshArgBuf *args) {
ecmcCANOpenAddMaster(args[0].sval,
args[1].ival,
args[2].ival,
args[3].ival,
args[4].ival);
}
/**
* EPICS iocsh shell command: ecmcCANOpenAddDevice
*/
void ecmcCANOpenAddDevicePrintHelp() {
printf("\n");
printf(" Use ecmcCANOpenAddDevice(<name>, <node id>)\n");
printf(" <name> : Name of device.\n");
printf(" <node id> : CANOpen node id of device.\n");
printf(" <NMT Heartbeat timeout ms> : Timeout for NMT Heartbeat.\n");
printf("\n");
}
int ecmcCANOpenAddDevice(const char* name, int nodeId,int heartTimeOutMs) {
if(!name) {
printf("Error: name.\n");
ecmcCANOpenAddDevicePrintHelp();
return asynError;
}
if(strcmp(name,"-h") == 0 || strcmp(name,"--help") == 0 ) {
ecmcCANOpenAddDevicePrintHelp();
return asynSuccess;
}
if(!can) {
printf("Plugin not initialized/loaded.\n");
return asynError;
}
if(heartTimeOutMs < 0) {
printf("Invalid NMT heartbeat timeout.\n");
return asynError;
}
try {
can->addDevice((uint32_t)nodeId,name,heartTimeOutMs);
}
catch(std::exception& e) {
printf("Exception: %s. Add device failed.\n",e.what());
return asynError;
}
return asynSuccess;
}
static const iocshArg initArg0_1 =
{ "Name", iocshArgString };
static const iocshArg initArg1_1 =
{ "Node Id", iocshArgInt };
static const iocshArg initArg2_1 =
{ "NMT Heart timeout ms", iocshArgInt };
static const iocshArg *const initArgs_1[] = { &initArg0_1,
&initArg1_1,
&initArg2_1};
static const iocshFuncDef initFuncDef_1 = { "ecmcCANOpenAddDevice", 3, initArgs_1 };
static void initCallFunc_1(const iocshArgBuf *args) {
ecmcCANOpenAddDevice(args[0].sval, args[1].ival, args[2].ival);
}
/**
* EPICS iocsh shell command: ecmcCANOpenAddSDO
*/
void ecmcCANOpenAddSDOPrintHelp() {
printf("\n");
printf(" Use ecmcCANOpenAddSDO(<name>, <node id>,.....)\n");
printf(" <name> : Name of master device.\n");
printf(" <node id> : CANOpen node id of device/master.\n");
printf(" <cob id tx> : CANOpen cob id of Tx of slave SDO.\n");
printf(" <cob id rx> : CANOpen cob id of Rx of slave SDO.\n");
printf(" <dir> : Direction 1=write and 2=read.\n");
printf(" <ODIndex> : OD index of SDO.\n");
printf(" <ODSubIndex> : OD sub index of SDO.\n");
printf(" <ODSize> : OS Size.\n");
printf(" <readSampleTimeMs>: Sample time for read in ms (write is always on demand).\n");
printf("\n");
}
int ecmcCANOpenAddSDO(const char* name,
int nodeId,
int cobIdTx,
int cobIdRx,
int dir,
int ODIndex,
int ODSubIndex,
int ODSize,
int readSampleTimeMs) {
if(!name) {
printf("Error: name.\n");
ecmcCANOpenAddSDOPrintHelp();
return asynError;
}
if(strcmp(name,"-h") == 0 || strcmp(name,"--help") == 0 ) {
ecmcCANOpenAddSDOPrintHelp();
return asynSuccess;
}
if(cobIdRx < 0) {
printf("Error: invalid cobIdRx.\n");
ecmcCANOpenAddSDOPrintHelp();
return asynError;
}
if(cobIdTx < 0) {
printf("Error: invalid cobIdTx.\n");
ecmcCANOpenAddSDOPrintHelp();
return asynError;
}
if(dir > 2 || dir <= 0) {
printf("Error: invalid dir.\n");
ecmcCANOpenAddSDOPrintHelp();
return asynError;
}
if(ODIndex < 0) {
printf("Error: invalid ODIndex.\n");
ecmcCANOpenAddSDOPrintHelp();
return asynError;
}
if(ODSubIndex < 0) {
printf("Error: invalid ODSubIndex.\n");
ecmcCANOpenAddSDOPrintHelp();
return asynError;
}
if(ODSize < 0) {
printf("Error: invalid ODSize.\n");
ecmcCANOpenAddSDOPrintHelp();
return asynError;
}
if(readSampleTimeMs < 0) {
printf("Error: invalid readSampleTimeMs.\n");
ecmcCANOpenAddSDOPrintHelp();
return asynError;
}
ecmc_can_direction tempDir = DIR_READ;
if(dir == 1) {
tempDir = DIR_WRITE;
}
try {
can->addSDO((uint32_t)nodeId,
cobIdTx,
cobIdRx,
tempDir,
ODIndex,
ODSubIndex,
ODSize,
readSampleTimeMs,
name);
}
catch(std::exception& e) {
printf("Exception: %s. Add PDO failed.\n",e.what());
return asynError;
}
return asynSuccess;
}
static const iocshArg initArg0_2 =
{ "Name", iocshArgString };
static const iocshArg initArg1_2 =
{ "Node Id", iocshArgInt };
static const iocshArg initArg2_2 =
{ "COB id TX", iocshArgInt };
static const iocshArg initArg3_2 =
{ "COB id RX", iocshArgInt };
static const iocshArg initArg4_2 =
{ "Direction", iocshArgInt };
static const iocshArg initArg5_2 =
{ "OD Index", iocshArgInt };
static const iocshArg initArg6_2 =
{ "OD sub index", iocshArgInt };
static const iocshArg initArg7_2 =
{ "OD size", iocshArgInt };
static const iocshArg initArg8_2 =
{ "Read sample time ms", iocshArgInt };
static const iocshArg *const initArgs_2[] = { &initArg0_2,
&initArg1_2,
&initArg2_2,
&initArg3_2,
&initArg4_2,
&initArg5_2,
&initArg6_2,
&initArg7_2,
&initArg8_2};
static const iocshFuncDef initFuncDef_2 = { "ecmcCANOpenAddSDO", 9, initArgs_2 };
static void initCallFunc_2(const iocshArgBuf *args) {
ecmcCANOpenAddSDO(args[0].sval,
args[1].ival,
args[2].ival,
args[3].ival,
args[4].ival,
args[5].ival,
args[6].ival,
args[7].ival,
args[8].ival);
}
/**
* EPICS iocsh shell command: ecmcCANOpenAddPDO
*/
void ecmcCANOpenAddPDOPrintHelp() {
printf("\n");
printf(" Use \"ecmcCANOpenAddPDO(<name>, <node id>\n");
printf(" <name> : Name of master device.\n");
printf(" <node id> : CANOpen node id of device/master.\n");
printf(" <cob id> : CANOpen cob id of PDO.\n");
printf(" <dir> : Direction 1=write and 2=read.\n");
printf(" <ODSize> : Size of PDO (max 8 bytes).\n");
printf(" <readTimeoutMs> : Readtimeout in ms.\n");
printf(" <writeCycleMs> : Cycle time for write (if <= 0 then only write on change).\n");
printf("\n");
}
int ecmcCANOpenAddPDO(const char* name,
int nodeId,
int cobId,
int dir,
int ODSize,
int readTimeoutMs,
int writeCycleMs) {
if(!name) {
printf("Error: name.\n");
ecmcCANOpenAddPDOPrintHelp();
return asynError;
}
if(strcmp(name,"-h") == 0 || strcmp(name,"--help") == 0 ) {
ecmcCANOpenAddPDOPrintHelp();
return asynSuccess;
}
if(dir > 2 || dir <= 0) {
printf("Error: invalid dir.\n");
ecmcCANOpenAddPDOPrintHelp();
return asynError;
}
if(ODSize < 0) {
printf("Error: invalid ODSize.\n");
ecmcCANOpenAddPDOPrintHelp();
return asynError;
}
if(readTimeoutMs < 0) {
printf("Error: invalid readTimeoutMs.\n");
ecmcCANOpenAddPDOPrintHelp();
return asynError;
}
if(writeCycleMs < 0) {
printf("Error: invalid writeCycleMs.\n");
ecmcCANOpenAddPDOPrintHelp();
return asynError;
}
ecmc_can_direction tempDir = DIR_READ;
if(dir == 1) {
tempDir = DIR_WRITE;
}
try {
can->addPDO((uint32_t)nodeId,
cobId,
tempDir,
ODSize,
readTimeoutMs,
writeCycleMs,name);
}
catch(std::exception& e) {
printf("Exception: %s. Add PDO failed.\n",e.what());
return asynError;
}
return asynSuccess;
}
static const iocshArg initArg0_3 =
{ "Name", iocshArgString };
static const iocshArg initArg1_3 =
{ "Node Id", iocshArgInt };
static const iocshArg initArg2_3 =
{ "COB Id", iocshArgInt };
static const iocshArg initArg3_3 =
{ "Direction", iocshArgInt };
static const iocshArg initArg4_3 =
{ "ODSize", iocshArgInt };
static const iocshArg initArg5_3 =
{ "Read Timeout ms", iocshArgInt };
static const iocshArg initArg6_3 =
{ "Write cycle ms", iocshArgInt };
static const iocshArg *const initArgs_3[] = { &initArg0_3,
&initArg1_3,
&initArg2_3,
&initArg3_3,
&initArg4_3,
&initArg5_3,
&initArg6_3
};
static const iocshFuncDef initFuncDef_3 = { "ecmcCANOpenAddPDO", 7, initArgs_3 };
static void initCallFunc_3(const iocshArgBuf *args) {
ecmcCANOpenAddPDO(args[0].sval,
args[1].ival,
args[2].ival,
args[3].ival,
args[4].ival,
args[5].ival,
args[6].ival);
}
/**
* Register all functions
*/
void ecmcCANPluginDriverRegister(void) {
iocshRegister(&initFuncDef_0, initCallFunc_0); // ecmcCANOpenAddMaster
iocshRegister(&initFuncDef_1, initCallFunc_1); // ecmcCANOpenAddDevice
iocshRegister(&initFuncDef_2, initCallFunc_2); // ecmcCANOpenAddSDO
iocshRegister(&initFuncDef_3, initCallFunc_3); // ecmcCANOpenAddPDO
}
epicsExportRegistrar(ecmcCANPluginDriverRegister);

View File

@@ -7,7 +7,6 @@
*
* Created on: Mar 22, 2020
* Author: anderssandstrom
* Credits to https://github.com/sgreg/dynamic-loading
*
\*************************************************************************/
@@ -32,6 +31,9 @@ void f_worker_write(void *obj) {
}
/** ecmc ecmcSocketCANWriteBuffer class
* Implements writing of can messages to a socket.
* Two buffers are used. While a thread writes to socket from one of teh buffers,
* data can still be added to the other buffer. Then teh buffers are switched.
*/
ecmcSocketCANWriteBuffer::ecmcSocketCANWriteBuffer(int socketId, int cfgDbgMode) {
memset(&buffer1_.frames,0,sizeof(struct can_frame)*ECMC_CAN_MAX_WRITE_CMDS);
@@ -43,9 +45,8 @@ ecmcSocketCANWriteBuffer::ecmcSocketCANWriteBuffer(int socketId, int cfgDbgMode)
destructs_ = 0;
bufferSwitchMutex_ = epicsMutexCreate();
lastWriteSumError_ = 0;
writePauseTime_.tv_sec = 0;
writePauseTime_.tv_nsec = 2e6; // 1ms
writePauseTime_.tv_nsec = 2e6; // 2ms
buffer1_.frameCounter = 0;
buffer2_.frameCounter = 0;
@@ -130,8 +131,10 @@ int ecmcSocketCANWriteBuffer::addWriteCAN( uint32_t canId,
return addWriteCAN(&frame);
}
int ecmcSocketCANWriteBuffer::getlastWritesError() {
return lastWriteSumError_;
int ecmcSocketCANWriteBuffer::getlastWritesErrorAndReset() {
int tempError = lastWriteSumError_;
lastWriteSumError_ = 0;
return tempError;
}
int ecmcSocketCANWriteBuffer::addToBuffer(can_frame *frame) {
@@ -145,23 +148,6 @@ int ecmcSocketCANWriteBuffer::addToBuffer(can_frame *frame) {
return 0;
}
//void ecmcSocketCANWriteBuffer::addToBuffer1(can_frame *frame) {
// printf("addToBuffer1\n");
// epicsMutexLock(bufferMutex1_);
// buffer1_.frame[buffer1_.frameCounter] = *frame;
// buffer1_.frameCounter++;
// epicsMutexUnlock(bufferMutex1_);
//}
//
//void ecmcSocketCANWriteBuffer::addToBuffer2(can_frame *frame) {
// printf("addToBuffer2\n");
// epicsMutexLock(bufferMutex2_);
// buffer2_.frame[buffer2_.frameCounter] = *frame;
// buffer2_.frameCounter++;
// epicsMutexUnlock(bufferMutex2_);
//}
//
int ecmcSocketCANWriteBuffer::writeBuffer() {
int errorCode = 0;
@@ -179,43 +165,6 @@ int ecmcSocketCANWriteBuffer::writeBuffer() {
return lastWriteSumError_;
}
//void ecmcSocketCANWriteBuffer::writeBuffer1() {
// //printf("writeBuffer1\n");
// int errorCode = 0;
// epicsMutexLock(bufferMutex1_);
// if(buffer1_.frameCounter==0) {
// return;
// }
// printf("writeBuffer1\n");
// for(int i=0; i<buffer1_.frameCounter;i++) {
// errorCode = writeCAN(&buffer1_.frame[i]);
// if(errorCode) {
// lastWriteSumError_ = errorCode;
// }
// }
// buffer1_.frameCounter = 0;
// epicsMutexUnlock(bufferMutex1_);
//}
//
//void ecmcSocketCANWriteBuffer::writeBuffer2() {
//
// int errorCode = 0;
// epicsMutexLock(bufferMutex2_);
// if(buffer2_.frameCounter==0) {
// return;
// }
// printf("writeBuffer2\n");
//
// for(int i=0; i<buffer2_.frameCounter;i++) {
// errorCode = writeCAN(&buffer2_.frame[i]);
// if(errorCode) {
// lastWriteSumError_ = errorCode;
// }
// }
// buffer2_.frameCounter = 0;
// epicsMutexUnlock(bufferMutex2_);
//}
int ecmcSocketCANWriteBuffer::switchBuffer() {
// ensure safe buffer switch
@@ -236,7 +185,14 @@ int ecmcSocketCANWriteBuffer::writeCAN(can_frame *frame){
// Maybe need to add the size to write here.. if struct is not full, hmm?!
int nbytes = write(socketId_, frame, sizeof(struct can_frame));
if(nbytes == -1) {
printf("ecmcSocketCAN: write() fail with error: %s (0x%x).\n", strerror(errno),errno);
return errno;
}
if (nbytes!= sizeof(struct can_frame)) {
printf("ecmcSocketCAN: write() fail with error: Incomplete write(), not a full can frame (0x%x).\n",ECMC_CAN_ERROR_WRITE_INCOMPLETE);
return ECMC_CAN_ERROR_WRITE_INCOMPLETE;
}

View File

@@ -71,7 +71,7 @@ class ecmcSocketCANWriteBuffer {
uint8_t data6,
uint8_t data7);
int addWriteCAN(can_frame *frame);
int getlastWritesError();
int getlastWritesErrorAndReset();
private:
static std::string to_string(int value);
@@ -90,7 +90,7 @@ class ecmcSocketCANWriteBuffer {
int writeBusy_;
int lastWriteSumError_;
int bufferIdAddFrames_;
timespec writePauseTime_;
timespec writePauseTime_;
};
#endif /* ECMC_SOCKETCAN_BUFFER_WRITE_H_ */