diff --git a/db/Beckhoff_5XXX/ecmcEL5032.substitutions b/db/Beckhoff_5XXX/ecmcEL5032.substitutions new file mode 100644 index 000000000..c607daa58 --- /dev/null +++ b/db/Beckhoff_5XXX/ecmcEL5032.substitutions @@ -0,0 +1,7 @@ +# use the EL5042 template, as the process image is identical +file "ecmcEL5042-chX.template" +{ + pattern {CH_ID} + {01 } + {02 } +} diff --git a/db/Beckhoff_5XXX/ecmcEL5042-chX.template b/db/Beckhoff_5XXX/ecmcEL5042-chX.template index d9e67eed4..5567421b6 100644 --- a/db/Beckhoff_5XXX/ecmcEL5042-chX.template +++ b/db/Beckhoff_5XXX/ecmcEL5042-chX.template @@ -1,3 +1,4 @@ +# TODO: rename to match EL5032 and EL5042, and potentially more. record(ai,"${ECMC_P}Enc${CH_ID}-PosAct"){ field(DESC, "$(HWTYPE): CH$(CH_ID): Actual position") field(PINI, "$(PINI=1)") diff --git a/examples/test/el5032/el5032.iocsh b/examples/test/el5032/el5032.iocsh new file mode 100644 index 000000000..c3b151dd6 --- /dev/null +++ b/examples/test/el5032/el5032.iocsh @@ -0,0 +1,21 @@ +require ecmccfg,kiveln + +${SCRIPTEXEC} ${ECMC_CONFIG_ROOT}addSlave.cmd, "HW_DESC=EL5032, SLAVE_ID=9" + +#- Reverse encoder direction 0x80p8:01: +#- ecmcConfigOrDie "Cfg.EcAddSdo(${ECMC_EC_SLAVE_NUM},0x8008,0x01,1,1)" + +#- offset, we need to set 8 bytes (int64), but ECMC only supports up to 4 bytes --> TODO: create issue +#- ecmcConfigOrDie "Cfg.EcAddSdo(${ECMC_EC_SLAVE_NUM},0x8008,0x12,5000,8)" + +#- switching of the supply voltage was _not_ tested. + +#- ############################################################################# +#- apply configuration +${SCRIPTEXEC} ${ECMC_CONFIG_ROOT}applyConfig.cmd +#- ############################################################################# +#- go active +${SCRIPTEXEC} ${ECMC_CONFIG_ROOT}setAppMode.cmd + +#- reset all errors +ecmcConfigOrDie "ControllerErrorReset()" diff --git a/hardware/Beckhoff_5XXX/EL/ecmcEL5032.cmd b/hardware/Beckhoff_5XXX/EL/ecmcEL5032.cmd new file mode 100644 index 000000000..26672c680 --- /dev/null +++ b/hardware/Beckhoff_5XXX/EL/ecmcEL5032.cmd @@ -0,0 +1,29 @@ +#-d /** +#-d \brief hardware script for EL5032 +#-d \details 2 channel EnDAT encoder interface +#-d \author Niko Kivel +#-d \file +#-d */ + +epicsEnvSet("ECMC_EC_HWTYPE" "EL5032") +epicsEnvSet("ECMC_EC_VENDOR_ID" "0x2") +epicsEnvSet("ECMC_EC_PRODUCT_ID" "0x13a83052") + +#- verify slave, including reset +ecmcFileExist(${ecmccfg_DIR}slaveVerify.cmd,1) +${SCRIPTEXEC} ${ecmccfg_DIR}slaveVerify.cmd "RESET=false" +#- slave returns an error on reset. Thus, we can't use the regualr `RESET=true` in this case +#- reset slave manually +# ==> disregard subsequent error for the SDO write to register 0x1011:01! <== +ecmcConfig "Cfg.EcWriteSdo(${ECMC_EC_SLAVE_NUM},0x1011,0x1,1684107116,4)" + +#- ########################################################### +#- ############ Config PDOS: Channel 1 + +ecmcConfigOrDie "Cfg.EcAddEntryComplete(${ECMC_EC_SLAVE_NUM},${ECMC_EC_VENDOR_ID},${ECMC_EC_PRODUCT_ID},2,3,0x1a00,0x6000,0x1,16,encoderStatus01)" +ecmcConfigOrDie "Cfg.EcAddEntryComplete(${ECMC_EC_SLAVE_NUM},${ECMC_EC_VENDOR_ID},${ECMC_EC_PRODUCT_ID},2,3,0x1a00,0x6000,0x11,64,positionActual01)" + +#- ############ Config PDOS: Channel 2 + +ecmcConfigOrDie "Cfg.EcAddEntryComplete(${ECMC_EC_SLAVE_NUM},${ECMC_EC_VENDOR_ID},${ECMC_EC_PRODUCT_ID},2,3,0x1a01,0x6010,0x1,16,encoderStatus02)" +ecmcConfigOrDie "Cfg.EcAddEntryComplete(${ECMC_EC_SLAVE_NUM},${ECMC_EC_VENDOR_ID},${ECMC_EC_PRODUCT_ID},2,3,0x1a01,0x6010,0x11,64,positionActual02)" diff --git a/scripts/python/EL5032.yaml b/scripts/python/EL5032.yaml new file mode 100644 index 000000000..440c9cf63 --- /dev/null +++ b/scripts/python/EL5032.yaml @@ -0,0 +1,28 @@ +0x9008: + - 0x01: { type: uint8, desc: Encoder type } + - 0x03: { type: uint8, desc: Reference system } + - 0x11: { type: string, desc: Encoder designation } + - 0x12: { type: string, desc: Encoder ident number } + - 0x13: { type: string, desc: Encoder serial number } + - 0x14: { type: uint32, desc: Multiturn resolution } + - 0x15: { type: uint32, desc: Singleturn resolution } + - 0x16: { type: uint32, desc: Measuring step } + - 0x21: { type: bool, desc: Light source error supported } + - 0x22: { type: bool, desc: Signal amplitude error supported } + - 0x23: { type: bool, desc: Position error supported } + - 0x24: { type: bool, desc: Overvoltage error supported } + - 0x25: { type: bool, desc: Undervoltage error supported } + - 0x26: { type: bool, desc: Overcurrent error supported } + - 0x27: { type: bool, desc: Battery error supported } + - 0x31: { type: bool, desc: Frequency warning supported } + - 0x32: { type: bool, desc: Temperature warning supported } + - 0x33: { type: bool, desc: Light source warning supported } + - 0x34: { type: bool, desc: Battery warning supported } + - 0x35: { type: bool, desc: Reference Mark warning supported } + - 0x36: { type: bool, desc: Acyclic mode warning supported } + - 0x37: { type: bool, desc: Limit position warning supported } + - 0x38: { type: bool, desc: Not ready warning supported } + - 0x39: { type: bool, desc: Diagnostic warning supported } + - 0x43: { type: bool, desc: Temperature 1 (external) supported } + - 0x44: { type: bool, desc: Temperature 2 (internal) supported } + - 0x51: { type: bool, desc: Clock pulse periods } diff --git a/scripts/python/readDataObjects.py b/scripts/python/readDataObjects.py new file mode 100755 index 000000000..95342fcda --- /dev/null +++ b/scripts/python/readDataObjects.py @@ -0,0 +1,60 @@ +#! /usr/bin/env python + +import argparse +import yaml +import json +import toml + +import subprocess + +def process_index(index, subindeces, master=0, slave=0) -> list: + root_cmd = f'ethercat -m{master} -p{slave} upload {index:#x}' + results = [] + for subindexDict in subindeces: + for subindex, value in subindexDict.items(): + data_type = value.get('type') + description = f'{subindex:#04x}: {value.get("desc", "")}' + cmd = f'{root_cmd} {subindex:#x} -t{data_type}' + result = subprocess.run(cmd.split(), stdout=subprocess.PIPE, text=True) + results.append(f'{description} = {result.stdout}') + return results + +# Create the parser +parser = argparse.ArgumentParser(description='Read a dictionary from a file.') + +# Add arguments +parser.add_argument('-y', '--yaml', type=str, help='YAML file to read') +parser.add_argument('-j', '--json', type=str, help='JSON file to read') +parser.add_argument('-t', '--toml', type=str, help='TOML file to read') +parser.add_argument('-m', '--master', type=int, default=0) +parser.add_argument('-p', '--position', type=int, default=1) + +# Parse the arguments +args = parser.parse_args() + +# Function to read and process the data +def read_and_process(file_path): + with open(file_path, 'r') as file: + if file_path.endswith('.yaml'): + data = yaml.safe_load(file) + elif file_path.endswith('.json'): + data = json.load(file) + elif file_path.endswith('.toml'): + data = toml.load(file) + else: + raise ValueError('Unsupported file type') + + for index, subindeces in data.items(): + print(f'INDEX ==== {index:#x} ====') + for response in process_index(index, subindeces, args.master, args.position): + print(response.strip()) + +# Call the function with the respective file path +if args.yaml: + read_and_process(args.yaml) +elif args.json: + read_and_process(args.json) +elif args.toml: + read_and_process(args.toml) +else: + parser.print_help()