From 36177f7b82dad5610df5a24b570da455f7d270fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20B=C3=B6gershausen?= Date: Tue, 4 Dec 2018 08:36:20 +0100 Subject: [PATCH 1/4] motorRecord.cc: Add ACCS field The problem: When a user changes the velocity by writing to the VELO field, but forgets to write to the ACCL field, the acceleration will change and may be too high. (Note: Many users are not aware of this side-effect). Solution: Introduce a field called ACCS, "Acceleration in seconds^2". Once the field is written, the ACCL field is updated and the acceleration is "locked". It is locked for changes of VELO. It can be be unlocked by writing to ACCL. If the acceleration is locked (or not) is stored in the field ACCU, which can be either "motorACCSused_Accl" or "motorACCSused_Accs". And in this sense ACCU is not a lock, but just keeps track which of the accelation fields ACCL or ACCS has been updated last, and should be used in the next movement. In any case an update to ACCS updated ACCL and the other way around. Thanks to Mark Rivers for this nice idea. --- docs/motorRecord.html | 13 ++++- motorApp/MotorSrc/motorRecord.cc | 90 ++++++++++++++++++++++++++++++- motorApp/MotorSrc/motorRecord.dbd | 17 ++++++ 3 files changed, 116 insertions(+), 4 deletions(-) diff --git a/docs/motorRecord.html b/docs/motorRecord.html index df4721b9..e0902527 100644 --- a/docs/motorRecord.html +++ b/docs/motorRecord.html @@ -242,6 +242,14 @@ below. DOUBLE acceleration time + + ACCS + R/W + Acceleration (EGU/s^2) + DOUBLE +
+ + ADEL R/W @@ -1675,7 +1683,8 @@ below. The motor record expects the hardware to produce a trapezoidal speed profile. That is, the motor speed is expected to increase linearly with time from the base speed, VBAS, to the full speed, VELO, in ACCL seconds. At the - end of a motion, the speed is expected to decrease similarly to VBAS.  + end of a motion, the speed is expected to decrease similarly to VBAS. + Note that the ACCS field can be use to specify the acceleration in EGU/s^2.  JVEL @@ -2329,7 +2338,7 @@ below. When one of these fields is set to 1, the record will immediately reset it to 0, and the motor will move (with backlash takeout if BDST is nonzero) by a distance TWV (in user coordinates) at the acceleration - specified by ACCL and at speed VELO. + specified by ACCL/ACCS and at speed VELO. TWV diff --git a/motorApp/MotorSrc/motorRecord.cc b/motorApp/MotorSrc/motorRecord.cc index 0129b879..4ddbeba4 100644 --- a/motorApp/MotorSrc/motorRecord.cc +++ b/motorApp/MotorSrc/motorRecord.cc @@ -475,6 +475,83 @@ static void callbackFunc(struct callback *pcb) } +static double accEGUfromVelo(motorRecord *pmr, double veloEGU) +{ + double vmin = pmr->vbas; + double vmax = fabs(veloEGU); + double acc; + /* ACCL or ACCS */ + if (pmr->accu == motorACCSused_Accs) + acc = pmr->accs; + else if (vmax > vmin) + acc = (vmax - vmin) / pmr->accl; + else + acc = vmax / pmr->accl; + + return acc; +} + +static void updateACCLfromACCS(motorRecord *pmr) +{ + if (pmr->accu != motorACCSused_Accs) + { + pmr->accu = motorACCSused_Accs; + db_post_events(pmr, &pmr->accu, DBE_VAL_LOG); + } + if (pmr->accs > 0.0) + { + double temp_dbl = pmr->velo / pmr->accs; + if (pmr->accl != temp_dbl) + { + pmr->accl = temp_dbl; + db_post_events(pmr, &pmr->accl, DBE_VAL_LOG); + } + } +} + +static void updateACCSfromACCL(motorRecord *pmr) +{ + double temp_dbl; + if (pmr->accu != motorACCSused_Accl) + { + pmr->accu = motorACCSused_Accl; + db_post_events(pmr, &pmr->accu, DBE_VAL_LOG); + } + temp_dbl = pmr->velo / pmr->accl; + if (pmr->accs != temp_dbl) + { + pmr->accs = temp_dbl; + db_post_events(pmr, &pmr->accs, DBE_VAL_LOG); + } +} + +static void updateACCL_ACCSfromVELO(motorRecord *pmr) +{ + if (pmr->accu == motorACCSused_Accs) + { + if (pmr->accs > 0.0) + { + double temp_dbl = pmr->velo / pmr->accs; + if (pmr->accl != temp_dbl) + { + pmr->accl = temp_dbl; + db_post_events(pmr, &pmr->accl, DBE_VAL_LOG); + } + } + } + else + { + double temp_dbl = pmr->velo / pmr->accl; + if (pmr->accs != temp_dbl) + { + pmr->accs = temp_dbl; + db_post_events(pmr, &pmr->accs, DBE_VAL_LOG); + } + } +} + + + /****************************************************************************** enforceMinRetryDeadband() @@ -859,7 +936,7 @@ static long postProcess(motorRecord * pmr) if (pmr->mip & MIP_JOG_STOP) { - double acc = (vel - vbase) > 0 ? ((vel - vbase)/ pmr->accl) : (vel / pmr->accl); + double acc = accEGUfromVelo(pmr, pmr->velo); if (vel <= vbase) vel = vbase + 1; @@ -2190,7 +2267,7 @@ static RTN_STATUS do_work(motorRecord * pmr, CALLBACK_VALUE proc_ind) double newpos = pmr->dval / pmr->mres; /* where to go */ double vbase = pmr->vbas / fabs(pmr->mres); /* base speed */ double vel = pmr->velo / fabs(pmr->mres); /* normal speed */ - double acc = (vel - vbase) > 0 ? ((vel - vbase) / pmr->accl) : (vel / pmr->accl); /* normal accel. */ + double acc = accEGUfromVelo(pmr, pmr->velo); /* * 'bpos' is one backlash distance away from 'newpos'. */ @@ -2595,6 +2672,7 @@ static long special(DBADDR *paddr, int after) /* new velo: make s agree */ case motorRecordVELO: range_check(pmr, &pmr->velo, pmr->vbas, pmr->vmax); + updateACCL_ACCSfromVELO(pmr); if ((pmr->urev != 0.0) && (pmr->s != (temp_dbl = pmr->velo / fabs_urev))) { @@ -2612,6 +2690,7 @@ static long special(DBADDR *paddr, int after) pmr->velo = temp_dbl; db_post_events(pmr, &pmr->velo, DBE_VAL_LOG); } + updateACCL_ACCSfromVELO(pmr); break; /* new bvel: make sbak agree */ @@ -2643,6 +2722,13 @@ static long special(DBADDR *paddr, int after) pmr->accl = 0.1; db_post_events(pmr, &pmr->accl, DBE_VAL_LOG); } + updateACCSfromACCL(pmr); + break; + + /* new accs */ + case motorRecordACCS: + db_post_events(pmr, &pmr->accs, DBE_VAL_LOG); + updateACCLfromACCS(pmr); break; /* new bacc */ diff --git a/motorApp/MotorSrc/motorRecord.dbd b/motorApp/MotorSrc/motorRecord.dbd index aa455cba..64c7bd13 100644 --- a/motorApp/MotorSrc/motorRecord.dbd +++ b/motorApp/MotorSrc/motorRecord.dbd @@ -69,6 +69,11 @@ menu(motorRMOD) { choice(motorRMOD_I,"In-Position") } +menu(motorACCSused) { + choice(motorACCSused_Accl, "Accl") + choice(motorACCSused_Accs, "Accs") +} + include "menuOmsl.dbd" recordtype(motor) { @@ -173,6 +178,18 @@ recordtype(motor) { interest(1) initial("0.2") } + field(ACCS,DBF_DOUBLE) { + prompt("Move Accel. (EGU/s^2)") + promptgroup(GUI_COMMON) + special(SPC_MOD) + interest(1) + } + field(ACCU,DBF_MENU) { + initial("Default") + prompt("ACCS used") + special(SPC_NOMOD) + menu(motorACCSused) + } field(BDST,DBF_DOUBLE) { prompt("BL Distance (EGU)") asl(ASL0) From 7b87f3b9b609ee4b070ef2efe0f78e158bd701f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20B=C3=B6gershausen?= Date: Tue, 4 Dec 2018 10:47:39 +0100 Subject: [PATCH 2/4] motorRecord: Handle ACCS != 0.0 in init_record When a database wants to use ACCS from the start, when the record is loaded into the IOC, the init_record() function needs to look at it. The new way is to set ACCS != 0.0 and ACCL == 0.0 and then ACCS takes over. The compatible (call it old ?) way is to have ACCS == 0.0, and then ACCL is used. --- motorApp/MotorSrc/motorRecord.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/motorApp/MotorSrc/motorRecord.cc b/motorApp/MotorSrc/motorRecord.cc index 4ddbeba4..8ae0e92b 100644 --- a/motorApp/MotorSrc/motorRecord.cc +++ b/motorApp/MotorSrc/motorRecord.cc @@ -3999,6 +3999,14 @@ static void check_speed_and_resolution(motorRecord * pmr) db_post_events(pmr, &pmr->sbak, DBE_VAL_LOG); db_post_events(pmr, &pmr->bvel, DBE_VAL_LOG); + if (pmr->accs && !pmr->accl) + { + /* ACCL == 0.0, ACCS is != 0.0 -> Use ACCS + This is a (possible) new way to configure a database. + Existing Db files will have ACCS == 0.0 and this + is backwards compatibleamd behaves as before */ + updateACCLfromACCS(pmr); + } /* Sanity check on acceleration time. */ if (pmr->accl == 0.0) { From 8d1891c5a7510391a68dd4a535260c133c2b92ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20B=C3=B6gershausen?= Date: Tue, 8 Jan 2019 10:40:30 +0100 Subject: [PATCH 3/4] motorRecord.cc: Make sure that ACCS/ACCU are initialized When the record is initialized, it could happen that the ACCS field was not initalized at all. Fix this: - Introduce a new value for motorACCSused, motorACCSused_Undef. This is == 0, and temporally used until the record is initilized. Using 0 as Undef will allow a (mis-) use in e.g. CSS to make the ACCS field invisable, whe ACCU is 0 (or not present at all in an older version of the motorRecord) - At the end of check_speed_and_resolution() set ACCS and ACCU, if needed. --- motorApp/MotorSrc/motorRecord.cc | 5 +++++ motorApp/MotorSrc/motorRecord.dbd | 1 + 2 files changed, 6 insertions(+) diff --git a/motorApp/MotorSrc/motorRecord.cc b/motorApp/MotorSrc/motorRecord.cc index 8ae0e92b..3c806bf9 100644 --- a/motorApp/MotorSrc/motorRecord.cc +++ b/motorApp/MotorSrc/motorRecord.cc @@ -4032,6 +4032,11 @@ static void check_speed_and_resolution(motorRecord * pmr) pmr->hvel = pmr->vbas; else range_check(pmr, &pmr->hvel, pmr->vbas, pmr->vmax); + /* Make sure that ACCS/ACCU are initialized */ + if (pmr->accu == motorACCSused_Undef) + { + updateACCSfromACCL(pmr); + } } /* diff --git a/motorApp/MotorSrc/motorRecord.dbd b/motorApp/MotorSrc/motorRecord.dbd index 64c7bd13..101e468f 100644 --- a/motorApp/MotorSrc/motorRecord.dbd +++ b/motorApp/MotorSrc/motorRecord.dbd @@ -70,6 +70,7 @@ menu(motorRMOD) { } menu(motorACCSused) { + choice(motorACCSused_Undef,"Undef") choice(motorACCSused_Accl, "Accl") choice(motorACCSused_Accs, "Accs") } From 02368bc98539749e037bb89c7e0d96b2d8955739 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20B=C3=B6gershausen?= Date: Thu, 10 Jan 2019 13:00:25 +0100 Subject: [PATCH 4/4] motorRecord.dbd: ACCU field should have "intial=Default" When the dbd file was loaded, EPICS base complained with "Error initializing motor.ACCU initial Default" Remove the 'initial("Default")' for the ACCU field. --- motorApp/MotorSrc/motorRecord.dbd | 1 - 1 file changed, 1 deletion(-) diff --git a/motorApp/MotorSrc/motorRecord.dbd b/motorApp/MotorSrc/motorRecord.dbd index 101e468f..06c64979 100644 --- a/motorApp/MotorSrc/motorRecord.dbd +++ b/motorApp/MotorSrc/motorRecord.dbd @@ -186,7 +186,6 @@ recordtype(motor) { interest(1) } field(ACCU,DBF_MENU) { - initial("Default") prompt("ACCS used") special(SPC_NOMOD) menu(motorACCSused)