Refactor IEEE488.2 registers to support SRQ
| | |
| | | |
| | | // Command error (e.g. syntax error) ch 21.8.9 |
| | | if ((err < -100) && (err > -199)) { |
| | | SCPI_RegSetBits(SCPI_REG_ESR, ESR_CER); |
| | | SCPI_RegSetBits(context, SCPI_REG_ESR, ESR_CER); |
| | | } |
| | | |
| | | // Execution Error (e.g. range error) ch 21.8.10 |
| | | if ((err < -200) && (err > -299)) { |
| | | SCPI_RegSetBits(SCPI_REG_ESR, ESR_EER); |
| | | SCPI_RegSetBits(context, SCPI_REG_ESR, ESR_EER); |
| | | } |
| | | |
| | | // Device specific error -300, -399 ch 21.8.11 |
| | | if ((err < -300) && (err > -399)) { |
| | | SCPI_RegSetBits(SCPI_REG_ESR, ESR_DER); |
| | | SCPI_RegSetBits(context, SCPI_REG_ESR, ESR_DER); |
| | | } |
| | | |
| | | // Query error -400, -499 ch 21.8.12 |
| | | if ((err < -400) && (err > -499)) { |
| | | SCPI_RegSetBits(SCPI_REG_ESR, ESR_QER); |
| | | SCPI_RegSetBits(context, SCPI_REG_ESR, ESR_QER); |
| | | } |
| | | |
| | | // Power on event -500, -599 ch 21.8.13 |
| | | if ((err < -500) && (err > -599)) { |
| | | SCPI_RegSetBits(SCPI_REG_ESR, ESR_PON); |
| | | SCPI_RegSetBits(context, SCPI_REG_ESR, ESR_PON); |
| | | } |
| | | |
| | | // User Request Event -600, -699 ch 21.8.14 |
| | | if ((err < -600) && (err > -699)) { |
| | | SCPI_RegSetBits(SCPI_REG_ESR, ESR_URQ); |
| | | SCPI_RegSetBits(context, SCPI_REG_ESR, ESR_URQ); |
| | | } |
| | | |
| | | // Request Control Event -700, -799 ch 21.8.15 |
| | | if ((err < -700) && (err > -799)) { |
| | | SCPI_RegSetBits(SCPI_REG_ESR, ESR_REQ); |
| | | SCPI_RegSetBits(context, SCPI_REG_ESR, ESR_REQ); |
| | | } |
| | | |
| | | // Operation Complete Event -800, -899 ch 21.8.16 |
| | | if ((err < -800) && (err > -899)) { |
| | | SCPI_RegSetBits(SCPI_REG_ESR, ESR_OPC); |
| | | SCPI_RegSetBits(context, SCPI_REG_ESR, ESR_OPC); |
| | | } |
| | | |
| | | if (context) { |
| | |
| | | #include "scpi_error.h" |
| | | #include "scpi_constants.h" |
| | | |
| | | /* register array */ |
| | | static scpi_reg_val_t regs[SCPI_REG_COUNT]; |
| | | |
| | | /** |
| | | * Update register value |
| | | * @param name - register name |
| | | */ |
| | | static void SCPI_RegUpdate(scpi_reg_name_t name) { |
| | | SCPI_RegSet(name, SCPI_RegGet(name)); |
| | | static void SCPI_RegUpdate(scpi_t * context, scpi_reg_name_t name) { |
| | | SCPI_RegSet(context, name, SCPI_RegGet(context, name)); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @param name - register name |
| | | * @return register value |
| | | */ |
| | | scpi_reg_val_t SCPI_RegGet(scpi_reg_name_t name) { |
| | | if (name < SCPI_REG_COUNT) { |
| | | return regs[name]; |
| | | scpi_reg_val_t SCPI_RegGet(scpi_t * context, scpi_reg_name_t name) { |
| | | if ((name < SCPI_REG_COUNT) && (context->registers != NULL)) { |
| | | return context->registers[name]; |
| | | } else { |
| | | return 0; |
| | | } |
| | |
| | | * @param name - register name |
| | | * @param val - new value |
| | | */ |
| | | void SCPI_RegSet(scpi_reg_name_t name, scpi_reg_val_t val) { |
| | | if (name >= SCPI_REG_COUNT) { |
| | | void SCPI_RegSet(scpi_t * context, scpi_reg_name_t name, scpi_reg_val_t val) { |
| | | bool_t srq = FALSE; |
| | | scpi_reg_val_t mask; |
| | | |
| | | if ((name >= SCPI_REG_COUNT) || (context->registers == NULL)) { |
| | | return; |
| | | } |
| | | |
| | | |
| | | // set register value |
| | | regs[name] = val; |
| | | context->registers[name] = val; |
| | | |
| | | switch (name) { |
| | | case SCPI_REG_STB: |
| | | if (val & (SCPI_RegGet(SCPI_REG_SRE) &~STB_SRQ)) { |
| | | mask = SCPI_RegGet(context, SCPI_REG_SRE); |
| | | mask &= ~STB_SRQ; |
| | | if (val & mask) { |
| | | val |= STB_SRQ; |
| | | srq = TRUE; |
| | | } else { |
| | | val &= ~STB_SRQ; |
| | | } |
| | | break; |
| | | case SCPI_REG_SRE: |
| | | SCPI_RegUpdate(SCPI_REG_STB); |
| | | SCPI_RegUpdate(context, SCPI_REG_STB); |
| | | break; |
| | | case SCPI_REG_ESR: |
| | | if (val & SCPI_RegGet(SCPI_REG_ESE)) { |
| | | SCPI_RegSetBits(SCPI_REG_STB, STB_ESR); |
| | | if (val & SCPI_RegGet(context, SCPI_REG_ESE)) { |
| | | SCPI_RegSetBits(context, SCPI_REG_STB, STB_ESR); |
| | | } else { |
| | | SCPI_RegClearBits(SCPI_REG_STB, STB_ESR); |
| | | SCPI_RegClearBits(context, SCPI_REG_STB, STB_ESR); |
| | | } |
| | | break; |
| | | case SCPI_REG_ESE: |
| | | SCPI_RegUpdate(SCPI_REG_ESR); |
| | | SCPI_RegUpdate(context, SCPI_REG_ESR); |
| | | break; |
| | | case SCPI_REG_QUES: |
| | | if (val & SCPI_RegGet(SCPI_REG_QUESE)) { |
| | | SCPI_RegSetBits(SCPI_REG_STB, STB_QES); |
| | | if (val & SCPI_RegGet(context, SCPI_REG_QUESE)) { |
| | | SCPI_RegSetBits(context, SCPI_REG_STB, STB_QES); |
| | | } else { |
| | | SCPI_RegClearBits(SCPI_REG_STB, STB_QES); |
| | | SCPI_RegClearBits(context, SCPI_REG_STB, STB_QES); |
| | | } |
| | | break; |
| | | case SCPI_REG_QUESE: |
| | | SCPI_RegUpdate(SCPI_REG_QUES); |
| | | SCPI_RegUpdate(context, SCPI_REG_QUES); |
| | | break; |
| | | case SCPI_REG_OPER: |
| | | if (val & SCPI_RegGet(SCPI_REG_OPERE)) { |
| | | SCPI_RegSetBits(SCPI_REG_STB, STB_OPS); |
| | | if (val & SCPI_RegGet(context, SCPI_REG_OPERE)) { |
| | | SCPI_RegSetBits(context, SCPI_REG_STB, STB_OPS); |
| | | } else { |
| | | SCPI_RegClearBits(SCPI_REG_STB, STB_OPS); |
| | | SCPI_RegClearBits(context, SCPI_REG_STB, STB_OPS); |
| | | } |
| | | break; |
| | | case SCPI_REG_OPERE: |
| | | SCPI_RegUpdate(SCPI_REG_OPER); |
| | | SCPI_RegUpdate(context, SCPI_REG_OPER); |
| | | break; |
| | | |
| | | |
| | |
| | | } |
| | | |
| | | // set updated register value |
| | | regs[name] = val; |
| | | context->registers[name] = val; |
| | | |
| | | if (srq && context->interface && context->interface->srq) { |
| | | context->interface->srq(context); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | * @param name - register name |
| | | * @param bits bit mask |
| | | */ |
| | | void SCPI_RegSetBits(scpi_reg_name_t name, scpi_reg_val_t bits) { |
| | | SCPI_RegSet(name, SCPI_RegGet(name) | bits); |
| | | void SCPI_RegSetBits(scpi_t * context, scpi_reg_name_t name, scpi_reg_val_t bits) { |
| | | SCPI_RegSet(context, name, SCPI_RegGet(context, name) | bits); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @param name - register name |
| | | * @param bits bit mask |
| | | */ |
| | | void SCPI_RegClearBits(scpi_reg_name_t name, scpi_reg_val_t bits) { |
| | | SCPI_RegSet(name, SCPI_RegGet(name) & ~bits); |
| | | void SCPI_RegClearBits(scpi_t * context, scpi_reg_name_t name, scpi_reg_val_t bits) { |
| | | SCPI_RegSet(context, name, SCPI_RegGet(context, name) & ~bits); |
| | | } |
| | | |
| | | /* ============ */ |
| | | |
| | | void SCPI_EventClear(void) { |
| | | void SCPI_EventClear(scpi_t * context) { |
| | | // TODO |
| | | SCPI_RegSet(SCPI_REG_ESR, 0); |
| | | SCPI_RegSet(context, SCPI_REG_ESR, 0); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @return |
| | | */ |
| | | scpi_result_t SCPI_CoreCls(scpi_t * context) { |
| | | (void) context; |
| | | SCPI_EventClear(); |
| | | SCPI_EventClear(context); |
| | | SCPI_ErrorClear(context); |
| | | SCPI_RegSet(SCPI_REG_OPER, 0); |
| | | SCPI_RegSet(SCPI_REG_QUES, 0); |
| | | SCPI_RegSet(context, SCPI_REG_OPER, 0); |
| | | SCPI_RegSet(context, SCPI_REG_QUES, 0); |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | |
| | | scpi_result_t SCPI_CoreEse(scpi_t * context) { |
| | | int32_t new_ESE; |
| | | if (SCPI_ParamInt(context, &new_ESE, TRUE)) { |
| | | SCPI_RegSet(SCPI_REG_ESE, new_ESE); |
| | | SCPI_RegSet(context, SCPI_REG_ESE, new_ESE); |
| | | } |
| | | return SCPI_RES_OK; |
| | | } |
| | |
| | | */ |
| | | scpi_result_t SCPI_CoreEseQ(scpi_t * context) { |
| | | (void) context; |
| | | SCPI_ResultInt(context, SCPI_RegGet(SCPI_REG_ESE)); |
| | | SCPI_ResultInt(context, SCPI_RegGet(context, SCPI_REG_ESE)); |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | |
| | | */ |
| | | scpi_result_t SCPI_CoreEsrQ(scpi_t * context) { |
| | | (void) context; |
| | | SCPI_ResultInt(context, SCPI_RegGet(SCPI_REG_ESR)); |
| | | SCPI_RegSet(SCPI_REG_ESR, 0); |
| | | SCPI_ResultInt(context, SCPI_RegGet(context, SCPI_REG_ESR)); |
| | | SCPI_RegSet(context, SCPI_REG_ESR, 0); |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | |
| | | */ |
| | | scpi_result_t SCPI_CoreOpc(scpi_t * context) { |
| | | (void) context; |
| | | SCPI_RegSetBits(SCPI_REG_ESR, ESR_OPC); |
| | | SCPI_RegSetBits(context, SCPI_REG_ESR, ESR_OPC); |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | |
| | | scpi_result_t SCPI_CoreSre(scpi_t * context) { |
| | | int32_t new_SRE; |
| | | if (SCPI_ParamInt(context, &new_SRE, TRUE)) { |
| | | SCPI_RegSet(SCPI_REG_SRE, new_SRE); |
| | | SCPI_RegSet(context, SCPI_REG_SRE, new_SRE); |
| | | } |
| | | return SCPI_RES_OK; |
| | | } |
| | |
| | | */ |
| | | scpi_result_t SCPI_CoreSreQ(scpi_t * context) { |
| | | (void) context; |
| | | SCPI_ResultInt(context, SCPI_RegGet(SCPI_REG_SRE)); |
| | | SCPI_ResultInt(context, SCPI_RegGet(context, SCPI_REG_SRE)); |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | |
| | | */ |
| | | scpi_result_t SCPI_CoreStbQ(scpi_t * context) { |
| | | (void) context; |
| | | SCPI_ResultInt(context, SCPI_RegGet(SCPI_REG_STB)); |
| | | SCPI_ResultInt(context, SCPI_RegGet(context, SCPI_REG_STB)); |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | |
| | | |
| | | #include "scpi_types.h" |
| | | |
| | | typedef uint16_t scpi_reg_val_t; |
| | | |
| | | typedef enum { |
| | | SCPI_REG_STB = 0, // Status Byte |
| | | SCPI_REG_SRE, // Service Request Enable Register |
| | | SCPI_REG_ESR, // Standard Event Status Register (ESR, SESR) |
| | | SCPI_REG_ESE, // Event Status Enable Register |
| | | SCPI_REG_OPER, // OPERation Status Register |
| | | SCPI_REG_OPERE, // OPERation Status Enable Register |
| | | SCPI_REG_QUES, // QUEStionable status register |
| | | SCPI_REG_QUESE, // QUEStionable status Enable Register |
| | | |
| | | /* last definition - number of registers */ |
| | | SCPI_REG_COUNT, |
| | | } scpi_reg_name_t; |
| | | |
| | | scpi_result_t SCPI_CoreCls(scpi_t * context); |
| | | scpi_result_t SCPI_CoreEse(scpi_t * context); |
| | | scpi_result_t SCPI_CoreEseQ(scpi_t * context); |
| | |
| | | #define ESR_PON 0x80 // Power On |
| | | |
| | | |
| | | scpi_reg_val_t SCPI_RegGet(scpi_reg_name_t name); |
| | | void SCPI_RegSet(scpi_reg_name_t name, scpi_reg_val_t val); |
| | | void SCPI_RegSetBits(scpi_reg_name_t name, scpi_reg_val_t bits); |
| | | void SCPI_RegClearBits(scpi_reg_name_t name, scpi_reg_val_t bits); |
| | | scpi_reg_val_t SCPI_RegGet(scpi_t * context, scpi_reg_name_t name); |
| | | void SCPI_RegSet(scpi_t * context, scpi_reg_name_t name, scpi_reg_val_t val); |
| | | void SCPI_RegSetBits(scpi_t * context, scpi_reg_name_t name, scpi_reg_val_t bits); |
| | | void SCPI_RegClearBits(scpi_t * context, scpi_reg_name_t name, scpi_reg_val_t bits); |
| | | |
| | | |
| | | #endif /* SCPI_IEEE488_H */ |
| | |
| | | */ |
| | | scpi_result_t SCPI_StatusQuestionableEventQ(scpi_t * context) { |
| | | // return value |
| | | SCPI_ResultInt(context, SCPI_RegGet(SCPI_REG_QUES)); |
| | | SCPI_ResultInt(context, SCPI_RegGet(context, SCPI_REG_QUES)); |
| | | |
| | | // clear register |
| | | SCPI_RegSet(SCPI_REG_QUES, 0); |
| | | SCPI_RegSet(context, SCPI_REG_QUES, 0); |
| | | |
| | | return SCPI_RES_OK; |
| | | } |
| | |
| | | */ |
| | | scpi_result_t SCPI_StatusQuestionableEnableQ(scpi_t * context) { |
| | | // return value |
| | | SCPI_ResultInt(context, SCPI_RegGet(SCPI_REG_QUESE)); |
| | | SCPI_ResultInt(context, SCPI_RegGet(context, SCPI_REG_QUESE)); |
| | | |
| | | return SCPI_RES_OK; |
| | | } |
| | |
| | | scpi_result_t SCPI_StatusQuestionableEnable(scpi_t * context) { |
| | | int32_t new_QUESE; |
| | | if (SCPI_ParamInt(context, &new_QUESE, TRUE)) { |
| | | SCPI_RegSet(SCPI_REG_QUESE, new_QUESE); |
| | | SCPI_RegSet(context, SCPI_REG_QUESE, new_QUESE); |
| | | } |
| | | return SCPI_RES_OK; |
| | | } |
| | |
| | | scpi_result_t SCPI_StatusPreset(scpi_t * context) { |
| | | (void) context; |
| | | // clear STATUS:... |
| | | SCPI_RegSet(SCPI_REG_QUES, 0); |
| | | SCPI_RegSet(context, SCPI_REG_QUES, 0); |
| | | return SCPI_RES_OK; |
| | | } |
| | |
| | | * @param buffer |
| | | * @param interface |
| | | */ |
| | | void SCPI_Init(scpi_t * context, scpi_command_t * command_list, scpi_buffer_t * buffer, scpi_interface_t * interface) { |
| | | context->cmdlist = command_list; |
| | | context->buffer.data = buffer->data; |
| | | context->buffer.length = buffer->length; |
| | | void SCPI_Init(scpi_t * context) { |
| | | context->buffer.position = 0; |
| | | context->interface = interface; |
| | | SCPI_ErrorInit(context); |
| | | } |
| | | |
| | |
| | | extern "C" { |
| | | #endif |
| | | |
| | | void SCPI_Init(scpi_t * context, scpi_command_t * command_list, scpi_buffer_t * buffer, scpi_interface_t * interface); |
| | | void SCPI_Init(scpi_t * context); |
| | | |
| | | int SCPI_Input(scpi_t * context, const char * data, size_t len); |
| | | int SCPI_Parse(scpi_t * context, const char * data, size_t len); |
| | |
| | | typedef enum _scpi_result_t scpi_result_t; |
| | | typedef struct _scpi_param_list_t scpi_param_list_t; |
| | | typedef struct _scpi_command_t scpi_command_t; |
| | | typedef scpi_result_t (*scpi_command_callback_t)(scpi_t *); |
| | | typedef scpi_result_t(*scpi_command_callback_t)(scpi_t *); |
| | | |
| | | /* scpi error queue */ |
| | | typedef void * scpi_error_queue_t; |
| | |
| | | typedef struct _scpi_special_number_def_t scpi_special_number_def_t; |
| | | typedef struct _scpi_number_t scpi_number_t; |
| | | |
| | | |
| | | /* IEEE 488.2 registers */ |
| | | typedef enum _scpi_reg_name_t scpi_reg_name_t; |
| | | typedef uint16_t scpi_reg_val_t; |
| | | |
| | | struct _scpi_param_list_t { |
| | | const scpi_command_t * cmd; |
| | | const char * parameters; |
| | |
| | | scpi_error_callback_t error; |
| | | scpi_write_t write; |
| | | scpi_command_callback_t reset; |
| | | scpi_command_callback_t test; |
| | | scpi_command_callback_t test; |
| | | scpi_command_callback_t srq; |
| | | }; |
| | | |
| | | struct _scpi_t { |
| | |
| | | int_fast16_t input_count; |
| | | bool_t cmd_error; |
| | | scpi_error_queue_t error_queue; |
| | | scpi_reg_val_t * registers; |
| | | }; |
| | | |
| | | enum _scpi_unit_t { |
| | |
| | | }; |
| | | |
| | | |
| | | enum _scpi_reg_name_t { |
| | | SCPI_REG_STB = 0, // Status Byte |
| | | SCPI_REG_SRE, // Service Request Enable Register |
| | | SCPI_REG_ESR, // Standard Event Status Register (ESR, SESR) |
| | | SCPI_REG_ESE, // Event Status Enable Register |
| | | SCPI_REG_OPER, // OPERation Status Register |
| | | SCPI_REG_OPERE, // OPERation Status Enable Register |
| | | SCPI_REG_QUES, // QUEStionable status register |
| | | SCPI_REG_QUESE, // QUEStionable status Enable Register |
| | | |
| | | /* last definition - number of registers */ |
| | | SCPI_REG_COUNT, |
| | | }; |
| | | |
| | | |
| | | #ifdef __cplusplus |
| | | } |
| | | #endif |
| | |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | | scpi_command_t scpi_commands[] = { |
| | | static scpi_command_t scpi_commands[] = { |
| | | /* IEEE Mandated Commands (SCPI std V1999.0 4.1.1) */ |
| | | { .pattern = "*CLS", .callback = SCPI_CoreCls,}, |
| | | { .pattern = "*ESE", .callback = SCPI_CoreEse,}, |
| | |
| | | SCPI_CMD_LIST_END |
| | | }; |
| | | |
| | | size_t SCPI_Write(scpi_t * context, const char * data, size_t len) { |
| | | static size_t SCPI_Write(scpi_t * context, const char * data, size_t len) { |
| | | (void) context; |
| | | return fwrite(data, 1, len, stdout); |
| | | } |
| | | |
| | | int SCPI_Error(scpi_t * context, int_fast16_t err) { |
| | | static int SCPI_Error(scpi_t * context, int_fast16_t err) { |
| | | (void) context; |
| | | |
| | | fprintf(stderr, "**ERROR: %d, \"%s\"\r\n", (int32_t) err, SCPI_ErrorTranslate(err)); |
| | | return 0; |
| | | } |
| | | |
| | | scpi_interface_t scpi_interface = { |
| | | static scpi_result_t SCPI_Srq(scpi_t * context) { |
| | | scpi_reg_val_t stb = SCPI_RegGet(context, SCPI_REG_STB); |
| | | fprintf(stderr, "**SRQ: 0x%X (%d)\r\n", stb, stb); |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | | |
| | | static scpi_interface_t scpi_interface = { |
| | | .write = SCPI_Write, |
| | | .error = SCPI_Error, |
| | | .reset = NULL, |
| | | .test = NULL, |
| | | .srq = SCPI_Srq, |
| | | }; |
| | | |
| | | #define SCPI_BUFFER_LENGTH 256 |
| | | char buffer[SCPI_BUFFER_LENGTH]; |
| | | #define SCPI_INPUT_BUFFER_LENGTH 256 |
| | | static char scpi_input_buffer[SCPI_INPUT_BUFFER_LENGTH]; |
| | | |
| | | scpi_buffer_t scpi_buffer = { |
| | | .length = SCPI_BUFFER_LENGTH, |
| | | .data = buffer, |
| | | // .data = (char[SCPI_BUFFER_LENGTH]){}, |
| | | static scpi_reg_val_t scpi_regs[SCPI_REG_COUNT]; |
| | | |
| | | |
| | | scpi_t scpi_context = { |
| | | .cmdlist = scpi_commands, |
| | | .buffer = { |
| | | .length = SCPI_INPUT_BUFFER_LENGTH, |
| | | .data = scpi_input_buffer, |
| | | }, |
| | | .interface = &scpi_interface, |
| | | .registers = scpi_regs, |
| | | }; |
| | | |
| | | scpi_t scpi_context; |
| | | |
| | | /* |
| | | * |
| | |
| | | (void) argv; |
| | | int result; |
| | | |
| | | SCPI_Init(&scpi_context, scpi_commands, &scpi_buffer, &scpi_interface); |
| | | SCPI_Init(&scpi_context); |
| | | |
| | | #define TEST_SCPI_INPUT(cmd) result = SCPI_Input(&scpi_context, cmd, strlen(cmd)) |
| | | |
| | |
| | | TEST_SCPI_INPUT("*ESE\r\n"); // cause error -109, missing parameter |
| | | TEST_SCPI_INPUT("*ESE 0x20\r\n"); |
| | | |
| | | TEST_SCPI_INPUT("*SRE 0xFF\r\n"); |
| | | |
| | | TEST_SCPI_INPUT("IDN?\r\n"); // cause error -113, undefined header |
| | | |
| | | TEST_SCPI_INPUT("SYST:ERR?\r\n"); |
| | |
| | | TEST_SCPI_INPUT("meas:volt:dc? 0.00001\r\n"); |
| | | |
| | | |
| | | //printf("%.*s %s\r\n", 3, "asdadasdasdasdas", "b"); |
| | | //printf("%.*s %s\r\n", 3, "asdadasdasdasdas", SCPI_RES_ERR"b"); |
| | | // interactive demo |
| | | //char smbuffer[10]; |
| | | //while (1) { |