Added custom changes EPICs fast boards drivers

This commit is contained in:
ferrog
2025-05-15 15:10:34 +00:00
parent 01dae2f39b
commit 13772b30aa
13 changed files with 900 additions and 10 deletions

View File

@@ -0,0 +1,97 @@
#+======================================================================
# $HeadURL$
# $Id$
#
# Project : JCSL-JAEA-GYRO-2012
#
# Description : StreamDevice protocol file for JASTEC SCM PS
# superconducting magnet power supply controller.
#
#RS232C protocol : Baudrate 38400 bps
# Data length 8 bit
# Stop bit 1 bit
# Parity None
#
#
# Author : Takashi Nakamoto, Cosylab
# Jack Oda, JAEA
# Kazuhide Uchida, Cosylab Japan
#
# Copyright (c) : 2013 Cosylab d.d.
# 2015 JAEA
# 2017 Cosylab Japan Inc.
#
#-======================================================================
Terminator = CR;
LockTimeout = 1000;
ReplyTimeout = 200;
### set commands ######################################################
# set target current [A]
setCurrent {
out ":I%#+09.4f";
in ":I%f";
}
# set sweep rate [A/min]
setSweep {
out ":S%#05.2f";
in ":S%f";
}
# set operation state
# L0 hold current state, L1 restart ramping current, L2 move to 0 A
setActivity {
out ":L%d";
in ":L%d";
}
### Read commands ##################################################
# read operation state
# 0:remote hold, 1:remote set, 2:remote down, 3:remote F.T. 4:remote zero
# 5:local hold, 6: local set, 7:local down, 8:local F.T., 9:local zero
readActivity {
out ":L?";
in ":L%d";
}
# read target current [A]
readTargCurrent {
out ":I?";
in ":%f";
}
# read output current [A]
readMeasCurrent {
out ":A?";
in ":%f";
}
# read current voltage [V]
readMeasVoltage {
out ":V?";
in ":%f";
}
# read target sweep rate [A/min]
readTargSweep {
out ":S?";
in ":%f";
}
# read sweep rate upper limit[A/min]
readSweepLim {
out ":R?";
in ":%f";
}
# read error status
readErrStat {
Separator = ",";
out ":E?";
in ":%39c"
}

View File

@@ -0,0 +1,59 @@
file "$(EPICS_ROOT)/db/pxi6259-module.template" {
pattern
{
AI_CONF,
ALARM_PATH,
BOARDTYPEIDX,
BOY_LEVEL,
CBS1,
CBS2,
DAQ_MODE,
MODULEIDX,
NUMSAMPLES,
POSTTRIG_SAMPLES,
PRETRIG_SAMPLES,
SAMPLERATE,
SRCCLK_CHAN,
SRCCLK_TYPE,
SWITCH_ACQ
}
{
"1",
"/CODAC_AlarmHandler/EC/EC-GN/EC-GN-HWCF",
"0",
"ITER-EC-GN-HWCF",
"EC",
"GN",
"1",
"1",
"5000",
"4000",
"2000",
"1000000",
"0",
"0",
"1"
}
{
"1",
"/CODAC_AlarmHandler/EC/EC-GN/EC-GN-HWCF",
"1",
"ITER-EC-GN-HWCF",
"EC",
"GN",
"1",
"0",
"5000",
"4000",
"2000",
"1000000",
"0",
"0",
"1"
}
}

View File

@@ -0,0 +1,80 @@
file "$(EPICS_ROOT)/db/pxie6368-module.template" {
pattern
{
AIPU,
AISR,
AISTSP,
AITB,
ALARM_PATH,
AOPU,
AOSTSP,
AOTB,
BOARDTYPEIDX,
BOY_LEVEL,
CBS1,
CBS2,
CHAIMO,
CHAOMO,
LDAICF,
LDAOCF,
MODULEIDX,
NO_SAMPLES,
POSTTRIG_SAMPLES,
PRETRIG_SAMPLES,
SRCCLK_CHAN,
SRCCLK_TYPE
}
{
"2",
"1000000",
"0",
"0",
"/CODAC_AlarmHandler/EC/EC-GN/EC-GN-HWCF",
"2",
"0",
"0",
"0",
"ITER-EC-GN-HWCF",
"EC",
"GN",
"2",
"2",
"0",
"0",
"1",
"1",
"1",
"0",
"0",
"0"
}
{
"2",
"1000000",
"0",
"0",
"/CODAC_AlarmHandler/EC/EC-GN/EC-GN-HWCF",
"2",
"0",
"0",
"1",
"ITER-EC-GN-HWCF",
"EC",
"GN",
"2",
"2",
"0",
"0",
"0",
"1",
"1",
"0",
"0",
"0"
}
}

View File

@@ -0,0 +1,27 @@
file "$(EPICS_ROOT)/db/nisync_device_info.template" {
pattern
{
ALARM_PATH,
BOARDTYPEIDX,
BOY_LEVEL,
CBS1,
CBS2,
MODULEIDX,
PREFIXBOARDTYPE,
SHORTBOARDTYPE
}
{
"/CODAC_AlarmHandler/EC/EC-GN/EC-GN-HWCF",
"0",
"ITER-EC-GN-HWCF",
"EC",
"GN",
"0",
"ni6683h_",
"6683"
}
}

View File

@@ -0,0 +1,62 @@
file "$(EPICS_ROOT)/db/pxi6528-module.template" {
pattern
{
ALARM_PATH,
BOARDTYPEIDX,
BOY_LEVEL,
CBS1,
CBS2,
FILINT_VAL,
MODULEIDX,
POWUPSTAT3,
POWUPSTAT4,
POWUPSTAT5,
RTSIINPROUT_VAL,
RTSIOUTROUT_VAL,
RTSIPCHANGE_VAL,
RTSIPWDG_VAL,
RTSIWDGTRIG_VAL,
WDGTOUT_VAL
}
{
"/CODAC_AlarmHandler/EC/EC-GN/EC-GN-HWCF",
"0",
"ITER-EC-GN-HWCF",
"EC",
"GN",
"0",
"1",
"255",
"255",
"255",
"0",
"0",
"0",
"0",
"0",
"0"
}
{
"/CODAC_AlarmHandler/EC/EC-GN/EC-GN-HWCF",
"1",
"ITER-EC-GN-HWCF",
"EC",
"GN",
"0",
"0",
"255",
"255",
"255",
"0",
"0",
"0",
"0",
"0",
"0"
}
}

View File

@@ -0,0 +1,249 @@
#+======================================================================
# $HeadURL$
# $Id$
#
# Project : C-QST-ITERGyrotron
#
# Description : StreamDevice protocol file for PCR500LE
# (Heater Power Supply)
# PSU manual is "http://www.kikusui.co.jp/kiku_manuals/P/PCR_LE/manual_LE_i.html".
#
#RS232C protocol : Baudrate 9600bps / (19200 bps) / 38400 bps
# Data length (8bit) / 7bit
# Stop bit (1bit) / 2bit
# Parity (None)
# Flow Ctrl OFF / (RTS-CTS)
#
# Author : Kazuhide Uchida (Cosylab Japan)
#
# Copyright (c) : 2017 Cosylab Japan Inc.
#
#-======================================================================
#InTerminator = LF;
#OutTerminator = LF;
Terminator = LF;
LockTimeout = 5000;
ReplyTimeout = 2000;
# Device status query ##########################
readDeviceID {
ExtraInput = Ignore;
out "*IDN?";
in "%s";
}
resetDevice {
out "*RST";
in "%s";
}
# Operation command #############################
setCoupling {
out "OUTP:COUP %{DC|AC|ACDC|EAC|EDC}";
in "%s";
}
getCoupling {
out "OUTP:COUP?";
in "%s";
}
### set output on/off
setOut {
out "OUTP %{ON|OFF|1|0}";
in "%d";
}
getOut {
out "OUTP?";
in "%d";
}
### set control source type (internal or external)
setSource {
out "FUNC:SOUR %{INT|EXT|SUM}";
}
getSource {
out "FUNC:SOUR?";
in "%s";
}
### Command to descide AC voltage control by external voltage.
setSourceON {
out "VOLT:EXT:SOUR %{NONE|VOLT}";
}
getSourceON {
out "VOLT:EXT:SOUR?";
in "%{NONE|VOLT}";
}
# DC output protocol set ########################
### output range
setVoltRange {
out "VOLT:RANG %d";
in "%d";
}
getVoltageRange {
out "VOLT:RANG?";
in "%d";
}
### DC voltage setpoint
setVOffs {
out "VOLTAGE:OFFS %f";
in "%f";
}
getVOffs {
out "VOLTAGE:OFFS?";
in "%f";
}
### DC voltage offset upper limit
setVOffsUL {
out "VOLTAGE:OFFS:LIM %f";
in "%f";
}
getVOffsUL {
out "VOLTAGE:OFFS:LIM?";
in "%f";
}
### DC voltage offset lower limit
setVOffsLL {
out "VOLTAGE:OFFS:LIM:LOW %f";
in "%f";
}
getVOffsLL {
out "VOLTAGE:OFFS:LIM:LOW?";
in "%f";
}
# AC output protocol set ########################
### AC frequency
setFreq {
out "FREQ %f";
in "%f";
}
getFreq {
out "FREQ?";
in "%f";
}
### AC voltage amp.
setVolt {
out "VOLT %f";
in "%f";
}
getVolt {
out "VOLT?";
in "%f";
}
setCurr {
out "CURR %f";
}
getCurr {
out "CURR?";
in "%f";
}
### Upper voltage limit
setUpVoltUL {
out "VOLTage:OFFS:LIM %f";
in "%f";
}
getUpVoltUL {
out "VOLT:OFFS:LIM?";
in "%f";
}
### Lower offset voltage limit
setLowVoltLL {
out "VOLT:OFFS:LIM:LOW %f";
in "%f";
}
getLowVoltLL {
out "VOLT:OFFS:LIM:LOW?";
in "%f";
}
# Query measured values##########################
getMeasDCV {
out "MEAS:VOLT:DC?";
in "%f";
}
getMeasDCI {
out "MEAS:CURR:DC?";
in "%f";
}
getMeasDCP {
out "MEAS:POW:DC?";
in "%f";
}
getMeasACV{
out "MEAS:VOLT:AC?";
in "%f";
}
getMeasACI{
out "MEAS:CURR:AC?";
in "%f";
}
getMeasACP{
out "MEAS:POW:AC?";
in "%f";
}
getMeasACF{
out "MEAS:FREQ?";
in "%f";
}
#################################################
# Enable Local operation
setLoc {
out "SYST:LOC";
}
setRem {
out "SYST:REM";
}
# Sequencial operation ##########################
# Set program
setProg {
out "PROGram:EDIT %d %{ON|OFF|0|1} %f 0 0 1 %f %f 0 1 0 0 1";
}
getProg {
out "PROGram:EDIT?";
in "%d %f %d %f %d %f %f %d %d %d %d %d";
}
# set sequence operation
setSeqStat {
out ":PROGram:STATe %{STOP|RUN|PAUSe|CONTinue}";
}
# get program state
getSeqStat {
out ":PROGram:EXEC?";
in "%s";
}
# set trigger source
setTrgSource {
out "TRIGer:PROGram:SOURce %{IMMediate|BUS}";
in "%s";
}
# Query system error ##########################
getError {
ExtraInput = Ignore;
out "SYST:ERR?";
in "%39c";
}
#################################################

View File

@@ -0,0 +1,154 @@
#+======================================================================
# $HeadURL$
# $Id$
#
# Project : C-QST-ITERGyrotron
#
# Description : StreamDevice protocol file for PWX1500ML
# (Heater Power Supply)
# PSU manual is "https://www.kikusui.co.jp/kiku_manuals/P/PWX/i_f_manual/japanese/00-intro.html".
# Ethernet Connection.
#
# Author : Kazuhide Uchida (Cosylab Japan)
#
# Copyright (c) : 2019 Cosylab Japan Inc.
#
#-======================================================================
#InTerminator = LF;
#OutTerminator = LF;
Terminator = LF;
LockTimeout = 1000;
ReplyTimeout = 100;
# Device status query ##########################
readDeviceID {
ExtraInput = Ignore;
out "*IDN?";
in "%s";
}
resetDevice {
out "*RST";
in "%s";
}
# Operation command #############################
### set output on/off
setOut {
out "OUTP %{ON|OFF|1|0}";
in "%d";
}
getOut {
out "OUTP?";
in "%d";
}
### set source on/off
setSourceON {
out "VOLT:EXT:SOUR %{NONE|VOLT}";
}
getSourceON {
out "VOLT:EXT:SOUR?";
in "%{NONE|VOLT}";
}
# DC output protocol set ########################
### OVP setpoint
setOVP {
out "VOLT:PROT %f";
in "%d";
}
getOVP {
out "VOLT:PROT?";
in "%f";
}
### DC voltage setpoint
setVolt {
out "VOLT %f";
}
getVolt {
out "VOLT?";
in "%f";
}
setCurr {
out "CURR %f";
}
getCurr {
out "CURR?";
in "%f";
}
# Query measured values##########################
getMeasDCV {
out "FETC:VOLT:DC?";
in "%f";
}
getMeasDCI {
out "FETC:CURR:DC?";
in "%f";
}
#getMeasV {
# out "MEAS:VOLT?"
# in "%f"
#}
#getMeasI {
# out "MEAS:VOLT?"
# in "%f"
#}
#################################################
# Enable Local operation
setRem {
out "SYST:COMM:RLST %{LOC|REM|RWL}";
}
getRem {
out "SYST:COMM:RLST?";
in "%{LOC|REM|RWL}";
}
# Sequencial operation ##########################
# Set program
setProg {
out "PROGram:EDIT %d %{ON|OFF|0|1} %f 0 0 1 %f %f 0 1 0 0 1";
}
getProg {
out "PROGram:EDIT?";
in "%d %f %d %f %d %f %f %d %d %d %d %d";
}
# set sequence operation
setSeqStat {
out ":PROGram:STATe %{STOP|RUN|PAUSe|CONTinue}";
}
# get program state
getSeqStat {
out ":PROGram:EXEC?";
in "%s";
}
# set trigger source
setTrgSource {
out "TRIGer:PROGram:SOURce %{IMMediate|BUS}";
in "%s";
}
# Query system error ##########################
getError {
ExtraInput = Ignore;
out "SYST:ERR?";
in "%39c";
}
#################################################

View File

@@ -68,12 +68,13 @@ if [[ ${ok} != "0" ]]; then
cp -rf ${project_name}-IN/.[^.]* ${project_name}/ cp -rf ${project_name}-IN/.[^.]* ${project_name}/
# cp -rf tmp_resources/* ${project_name}/src/main/resources # cp -rf tmp_resources/* ${project_name}/src/main/resources
# rm -rf tmp_resources # rm -rf tmp_resources
mkdir ${project_name,,}-sdd-in/SDD_IN/templates /bin/cp -rf /opt/codac/marte2-extensions/templates ${project_name,,}-sdd-in/SDD_IN/
/bin/cp -f /opt/codac/marte2-extensions/templates/* ${project_name,,}-sdd-in/SDD_IN/templates /bin/cp -f templates/* ${project_name,,}-sdd-in/SDD_IN/templates
/bin/cp -f patches/* ${project_name,,}-sdd-in/SDD_IN/templates
cd ${project_name,,}-sdd-in/SDD_IN cd ${project_name,,}-sdd-in/SDD_IN
./sdd-generate.sh ./sdd-generate.sh
rm -rf templates
fi fi
cid=$((cid+1)) cid=$((cid+1))
@@ -85,15 +86,19 @@ cd ${current_dir}
ok=$(echo ${mask} | cut -c ${cid}) ok=$(echo ${mask} | cut -c ${cid})
#if [[ ${ok} != "0" ]]; then if [[ ${ok} != "0" ]]; then
# cat patches/EC-GN-PCF0CORE_sddPreDriverConf.patch ${project_name}/src/main/epics/iocBoot/iocEC-GN-PCF0CORE/sddPreDriverConf.cmd > sddPreDriverConf_patched.cmd cat patches/EC-GN-PCF0CORE_sddPreDriverConf.patch ${project_name}/src/main/epics/iocBoot/iocEC-GN-PCF0CORE/sddPreDriverConf.cmd > sddPreDriverConf_patched.cmd
# mv -f sddPreDriverConf_patched.cmd ${project_name}/src/main/epics/iocBoot/iocEC-GN-PCF0CORE/sddPreDriverConf.cmd mv -f sddPreDriverConf_patched.cmd ${project_name}/src/main/epics/iocBoot/iocEC-GN-PCF0CORE/sddPreDriverConf.cmd
# cat patches/EC-GN-PCF0CORE_userPreDriverConf.patch ${project_name}/src/main/epics/iocBoot/iocEC-GN-PCF0CORE/userPreDriverConf.cmd > userPreDriverConf_patched.cmd cat patches/EC-GN-PCF0CORE_userPreDriverConf.patch ${project_name}/src/main/epics/iocBoot/iocEC-GN-PCF0CORE/userPreDriverConf.cmd > userPreDriverConf_patched.cmd
# mv -f userPreDriverConf_patched.cmd ${project_name}/src/main/epics/iocBoot/iocEC-GN-PCF0CORE/userPreDriverConf.cmd mv -f userPreDriverConf_patched.cmd ${project_name}/src/main/epics/iocBoot/iocEC-GN-PCF0CORE/userPreDriverConf.cmd
sed -i "s/#__patch/$(cat patches/EC-GN-PCF0CORE_dbToLoad.patch | sed ':a;N;$!ba;s/\n/__nl__/g')/g" ${project_name}/src/main/epics/iocBoot/iocEC-GN-PCF0CORE/dbToLoad.cmd
sed -i "s/__nl__/\n/g" ${project_name}/src/main/epics/iocBoot/iocEC-GN-PCF0CORE/dbToLoad.cmd
#fi fi
cid=$((cid+1)) cid=$((cid+1))

View File

@@ -0,0 +1,4 @@
dbLoadTemplate("PCF0-nisync-device-info.substitution")
dbLoadTemplate("PCF0-pxi6528-module.substitution")
dbLoadTemplate("PCF0-NI6368-module.substitution")
dbLoadTemplate("PCF0-NI6259-module.substitution")

View File

@@ -0,0 +1,63 @@
#============================================================================
# NI-6259 DAQ I/O Module driver configuration commands
#============================================================================
# Reference: ITER_D_3DEY52 v1.3 - NI PXI-6259 EPICS Driver Users Guide
# For analogue input, analogue output, waveform, initialize using below function
# pxi6259_ai_init(uint8 cardnumber, uint32 range, uint32 clk_source, uint32 clk_edge);
# Example: pxi6259_ai_init(0, 1, 0, 0)
# For binary input, binary output, multi-bit binary input, multi bit binary output, initialize using below function
# pxi6259init(uint8 cardnumber, uint32 portmask0, uint8 portmask1, uint8 portmask2);
# Example: pxi6259_bio_init(0, 0xFF000000, 0xFF, 0xFF)
pxi6259_ai_init(0, 1, 0, 0 )
pxi6259_bio_init(0, 0x0, 0x0, 0x0 )
pxi6259_ai_init(1, 1, 0, 0 )
pxi6259_bio_init(1, 0x0, 0x0, 0x0 )
#============================================================================
# NI-6682 Timing and Synchronization I/O Module driver configuration commands
#============================================================================
# Reference ITER_D_33Q5TX v1.7 - NI Sync EPICS Driver Users Guide
# nisyncDrvInit(string port, char* type, int cardNumber);
# Example: nisyncDrvInit("S0", "PXI-6682", "0");
# Example: nisyncDrvInit("S0", "PXI-6683H", "0");
# nisyncTimeInit(int cardID, char* type, int cardNumber);
# Example: nisyncTimeInit("0", "PXI-6682", "0")
# Example: nisyncTimeInit("0", "PXI-6683H", "0")
nisyncDrvInit("ni6683h_0", "PXI-6683H", "0")
nisyncTimeInit("0", "PXI-6683H", "0")
#============================================================================================
# NI-6368 X Series - Multifunction Data Acquisition I/O Module driver configuration commands
#============================================================================================
# Reference ITER_D_3P4N3R v1.2 - NI X Series EPICS Driver Users Guide
# nixseriesInit(char *portName, char *nix6368Card);
# Example: nixseriesInit("ni6368_0", "/dev/ni6368.0");
nixseriesInit("ni6368_0", "/dev/pxie-6368.1")
nixseriesInit("ni6368_1", "/dev/pxie-6368.0")
#============================================================================
# NI-6528 DAQ I/O Module driver configuration commands
#============================================================================
# Reference ITER_D_433VEW - NI PXI-6528 EPICS Driver User's Manual
# ni6528_init(char *portName, char *ni6528Card);
# Example: pxi6528_init("ni6528_0", "/dev/ni6528.0")
# asynSetTraceMask("<port name>",0,255)
# Example: asynSetTraceMask("pxi6528_0",0,255)
# pxi6528_reset(char *portName)
# Example: pxi6528_reset("pxi6528_0")
pxi6528_init("ni6528_0", "/dev/pxi6528.0")
# asynSetTraceMask("ni6528_0",0,255)
# pxi6528_reset("ni6528_0")
pxi6528_init("ni6528_1", "/dev/pxi6528.1")
# asynSetTraceMask("ni6528_1",0,255)
# pxi6528_reset("ni6528_1")

View File

@@ -0,0 +1,64 @@
############################################################################
## User provided PLC or fast controller driver pre configuration
############################################################################
### PXIe-6368 setup
## AO Port ##
asynXseriesResetAoConfiguration("ni6368_0")
asynXseriesStaticAo("ni6368_0")
asynXseriesAddAoChannel("ni6368_0", 0, 0)
asynXseriesAddAoChannel("ni6368_0", 1, 0)
asynXseriesAddAoChannel("ni6368_0", 2, 0)
asynXseriesAddAoChannel("ni6368_0", 3, 0)
asynXseriesLoadAoConfiguration("ni6368_0")
asynXseriesStartAo("ni6368_0")
asynXseriesResetAoConfiguration("ni6368_1")
asynXseriesStaticAo("ni6368_1")
asynXseriesAddAoChannel("ni6368_1", 0, 0)
asynXseriesAddAoChannel("ni6368_1", 1, 0)
asynXseriesAddAoChannel("ni6368_1", 2, 0)
asynXseriesAddAoChannel("ni6368_1", 3, 0)
asynXseriesLoadAoConfiguration("ni6368_1")
asynXseriesStartAo("ni6368_1")
## AI Port ##
### PXI-6259 setup
pxi6259_ai_start_trig(0, 1, 0, 0) #boardnumber, enable, trigger_source, trigger_edge
pxi6259_ai_start_trig(1, 1, 0, 0) #boardnumber, enable, trigger_source, trigger_edge
### PXI-6528 setup
### set change detection to both
pxi6528_set_port_change_detection("ni6528_0", 0, 255, 255)
pxi6528_set_port_change_detection("ni6528_0", 1, 255, 255)
pxi6528_set_port_change_detection("ni6528_0", 2, 255, 255)
pxi6528_set_port_change_detection("ni6528_1", 0, 255, 255)
pxi6528_set_port_change_detection("ni6528_1", 1, 255, 255)
pxi6528_set_port_change_detection("ni6528_1", 2, 255, 255)
### enable IO intr mode
pxi6528_enable_io_intr_scanning("ni6528_0")
pxi6528_enable_io_intr_scanning("ni6528_1")
### Stream Device setup ####################################
### set appropriate IP address and port number
drvAsynIPPortConfigure("CCPS1", "192.168.5.2:5025")
drvAsynIPPortConfigure("FHPS1", "192.168.5.3:6000")
drvAsynIPPortConfigure("MC1", "192.168.5.4:6000")
drvAsynIPPortConfigure("GC1", "192.168.5.5:6000")
drvAsynIPPortConfigure("CCPS2", "192.168.5.6:5025")
drvAsynIPPortConfigure("FHPS2", "192.168.5.7:6000")
drvAsynIPPortConfigure("MC2", "192.168.5.8:6000")
drvAsynIPPortConfigure("GC2", "192.168.5.9:6000")

View File

@@ -0,0 +1,26 @@
#======================================================================
# Loading DBs
#======================================================================
cd $(TOP)/db
@DBxx
#======================================================================
# Loading Substitution Files
#======================================================================
cd $(TOP)/iocBoot/$(IOC)
@SUBx
#__patch
#======================================================================
# PLC Communication Monitoring PVs DB Loading
#======================================================================
cd $(EPICS_ROOT)/db
@PLCM
#======================================================================
# IOC Monitor
#======================================================================
cd $(EPICS_ROOT)/db
#dbLoadRecords("iocmon.db","CBS=@CBS1-@CBS2-SYSM, CTRLTYPE=@CTRT, IDX=@IDNx, IOCTYPE=@IOCT, PP=@PBS2, PPPP=@PBS1, NNNN=@PBS4, TTT=@PBS3")
#- End-of-file marker - do not delete or add lines below!