Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ticket 4831 cryosms simple start #2

Merged
merged 23 commits into from
Feb 14, 2020

Conversation

LilithCole
Copy link
Contributor

Description of work

IOC now performs startup logic

To test

ISISComputingGroup/IBEX#4831

Acceptance criteria

new tests pass


Code Review

Functional Tests

  • IOC responds correctly in:
    • Devsim mode
    • Recsim mode
    • Real device, if available
  • Supplementary IOCs (..._0n where n>1) run correctly
  • Log files do not report undefined macros (serach for macLib: macro to find instances of macLib: macro [macro name] is undefined...

@LilithCole LilithCole changed the base branch from Ticket651_Add_IOC_CRYOSMS to master November 14, 2019 15:40
Copy link
Contributor

@Alistair-McGann-Tessella Alistair-McGann-Tessella left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very complicated IOC. The structure of the db file is good and it seems to cover most of the funcitonality required well. Most of the changes suggested are minor bug fixes and documentation, which is pretty good given the complexity of the device. 👍

I think it would be useful to add to the wiki page which parameters we expect the users to change, and a brief comment saying that IOC initialisation is done in the asyn driver and why. This will make it clearer to someone supporting this device.

It is worth thinking if any of the PVs should be labelled as interesting and archived.

When I ran the IOC system tests there was quite a lot of errors because of mismatches in the protocols which normally we wouldn't expect to see. There might need to be some work on the emulator and the protocol files to fix these bugs.

else {
const char *magMode = "Non Persistent";

status = putDb("MAGNET:MODE", &magMode);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what the behaviour will be here, it appears that a string is getting written to a binary (bo) record?


}

if (std::getenv("USE_SWITCH") == "Yes" && (std::getenv("SWITCH_TEMP_PV") == NULL || std::getenv("SWITCH_HIGH") == NULL || std::getenv("SWITCH_LOW") == NULL ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The switching variables are different here to the specification documentation. SWITCH_TIMEOUT is missing from this list, and SWITCH_TOLERANCE, SWITCH_TEMP_TOLERANCE, HEATER_OUT have been added?

}

setHeaterValue {
out "S H %f";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is different to the design document, which specifies SH.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the design document is incorrect in this instance, command is indeed S H

record(scalcout, "$(P)OUTPUT")
{
field(DESC, "Output from the PSU in $(DISPLAY_UNIT)")
field(FLNK, "$(P)GAUSS")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pointing to a PV which does not exist. Is this PV missing or should it be pointing somewhere else?

else {
return asynSuccess;
}
}

asynStatus CRYOSMSDriver::onStart()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's important that the logic here is tested so we are aware if we introduce a bug at a later date. I recommend splitting the if statements up into logical chunks and using googletest to unit test each chunk.

{
field(DESC, "Raw value returned from the PSU, A or T")# returned during a “GO” command
field(DTYP, "stream")
field(INP, "@cryosms.proto getOutput($(P)) $(PORT)")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This protocol also has a mismatch with the emulator, it looks like a CRLF at the end of the return statement. See the devsim logs.

{
field(DESC, "The status of the ramp from the PSU")
field(DTYP, "stream")
field(INP, "@cryosms.proto getRampStatus $(PORT)")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mismatches when running against emulator

@@ -95,8 +448,8 @@ extern "C"
}
// EPICS iocsh shell commands

static const iocshArg initArg0 = { "portName", iocshArgString }; ///< A name for the asyn driver instance we will create - used to refer to it from EPICS DB files
static const iocshArg initArg1 = { "devicePrefix", iocshArgString };
static const iocshArg initArg0 = { "portName", iocshArgString }; ///< Port to connect to
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Much clearer

else {
return asynSuccess;
}
}

asynStatus CRYOSMSDriver::onStart()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When started through the IOC test framework these were the commands issued over asyn (logged using an asyn trace mask). This is a different order to what I was expecting from the specification for the cryo sms:

SET TPA 0.037000
T OFF
SET MAX 135.000000
SET RAMP 1.120000
P
HEATER
GET SIGN
T ON
T
RAMP STATUS
GET O
GET RATE
T

If this is the expected command set on booting with the parameters in the IOC test framework this should be noted in the documentation

@@ -2,15 +2,28 @@
# Database and stream protocol file for Cryogenic Ltd SMS series superconducting magnet controller commands
#

record(bi, "$(P)SIM")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SIM and DISABLE records are common to all IOCs to make sure that records behave correctly in recsim. Is there a reason that they have been removed?

Copy link
Contributor

@Alistair-McGann-Tessella Alistair-McGann-Tessella left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much tidier now, the bulk of my comments fall in to either documentation (e.g. docstrings for the new functions) and error handling (e.g. adding errors to the IOC log file). The google tests are a really useful tool to keep this maintainable in the future.

CRYOSMSApp/src/CRYOSMSDriver.cpp Show resolved Hide resolved

asynStatus CRYOSMSDriver::checkMaxVolt()
{
asynStatus status;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

status is the return value for this function, but I can't see where it's modified here. Perhaps this should be initialised to asynSuccess or similar? This applies to other functions

asynStatus CRYOSMSDriver::checkWriteUnit()
{
asynStatus status;
int trueVal = 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use c++ booleans instead of defining your own?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to set the pvs to their desired states we need to pass a reference to the required integers. I chose to name them trueVal and falseVal purely for readability.


### RAMP:

record(bo, "$(P)RAMP:LEADS")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this record need ONAM and ZNAM set to true/false?

else {
status = readFile(envVarMap.at("RAMP_FILE"));
if (status != asynSuccess) {
this->writeDisabled = TRUE;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this condition also have something written to the STAT pv?

CRYOSMSApp/src/CRYOSMSDriver.cpp Show resolved Hide resolved
ASSERT_EQ(testDriver->writeDisabled, 0);
}

TEST_F(StartupTests, test_GIVEN_use_switch_yes_WHEN_a_required_value_null_THEN_write_disabled)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, tests like this would be parameterised such that we know that all combinations of NULL/not NULL work for all the environment variables. However, testing one is absolutely good enough, especially given the size of the ticket. I don't suggest these changes unless it's a trivial (<20 minute) job

int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a new line at the end of the file

Comment on lines 67 to 68
pRate_ = (epicsFloat64 *)calloc(INIT_ROW_NUM, sizeof(epicsFloat64));
pMaxT_ = (epicsFloat64 *)calloc(INIT_ROW_NUM, sizeof(epicsFloat64));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not immediately obvious what these variables do and why they are constructed like this, a comment explaining this would be useful. Becuase they call calloc, it's also important to call free after they are no longer useful to stop a memory leak.

"USE_MAGNET_TEMP", "MAGNET_TEMP_PV", "MAX_MAGNET_TEMP", "MIN_MAGNET_TEMP", "COMP_OFF_ACT", "NO_OF_COMP", "MIN_NO_OF_COMP_ON", "COMP_1_STAT_PV", "COMP_2_STAT_PV", "RAMP_FILE" };
for (std::string envVar : envVarsNames)
{
envVarMap.insert(std::pair<std::string, const char* >(envVar, std::getenv(envVar.c_str())));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 This is a really good way of ensuring that all the environment variables are available here and can be modified by the tests

If not supplied, disables puts and posts relevant status message
*/
{
asynStatus status;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initialise to asynSuccess?

{
/* Checks if the user wants to send data to the PSU in units of amps. Sends this choice to the machine if so, otherwise defaults to tesla.
*/
asynStatus status;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initialise to asynSuccess

FAST:ZERO and RAMP:LEADS to 0 and disable them.
*/
{
asynStatus status;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initialise

into memory, then read the current field value from the device and send back an appropriate ramp rate based on the ramp table.
*/
{
asynStatus status;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initialise

ASSERT_EQ(testDriver->writeDisabled, 1);
}

TEST_F(StartupTests, test_GIVEN_allow_persist_yes_WHEN_required_values_exist_THEN_write_enabled)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can there be a test for the case when allow_persist isn't set to yes? Could also be applied to other functions

}
TEST_F(StartupTests, test_GIVEN_IOC_WHEN_use_magnet_temp_not_yes_THEN_nothing_happens)
{
testDriver->envVarMap["USE_MAGNET_TEMP"] = "Yes";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sets use_magnet_temp to yes?

@Alistair-McGann-Tessella Alistair-McGann-Tessella merged commit cb96139 into master Feb 14, 2020
@FreddieAkeroyd FreddieAkeroyd deleted the Ticket_4831_CRYOSMS_simple_start branch September 17, 2024 23:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants