plc_read( ), plc_write( ) -- Master Mode

int plc_read(PLC *plc_ptr, int j_op, char *pc_addr, void *p_data, int j_length, int j_timeout, char *pc_format)

int plc_write(PLC *plc_ptr, int j_op, char *pc_addr, void *p_data, int j_length, int j_timeout, char *pc_format)

In master mode (solicited communication), these functions perform basic operations on the PLC, such as reading and writing data registers, coils, strings, and so forth.  Each function specifies the PLC address, application buffer, size of that buffer, a timeout (in milliseconds) for the operation to complete, and a format string describing the data structure, used only when converting between big- and little-endian byte-order.

 

Arguments

PLC *plc_ptr

The PLC communications object as returned from the plc_open( ) call.

int j_op

The type of operation to perform on the PLC -- usually PLC_RREG for reading 2-byte registers, or PLC_WREG for writing 2-byte registers.  The operations supported by a PLC are module-specific.  This option is discussed in more detail for each PLC module.

The target memory or I/O address for reading and writing data on the PLC.  If a soft PLC is opened, then this is the name of a soft address as defined in the PLC’s point-configuration file.  Otherwise, this is a physical address that corresponds to an actual memory location or I/O point on the PLC.

If the first character of pc_addr is a ! symbol, then the address following the ! refers to a physical address on the PLC regardless if the PLC was opened as a physical or soft PLC.  The soft-point table lookup is subsequently bypassed for this operation.

void *p_data

Pointer to the beginning of a buffer that either copies data from the PLC during a plc_read( ), or writes data to the PLC during a plc_write( ).  This variable can point to a data array of any size (char, short, int, long, etc.), including structures of data containing different sized members.

By default, PLCIO assumes the application is working with 16-bit short integers, typical of PLC communication.  Each coil and input is treated as a short integer (with non-zero values treated as coil-energized).

int j_length

The length, in bytes, of the data to send during a plc_write( ), or the maximum data to retrieve during a plc_read( ).  This value should be evenly divisible by the element size of its data members.

When referencing a soft address, an error can occur if this value is larger than the limitation placed in the point-configuration file.  The maximum value for j_length is defined by the constant PLC_CHAR_MAX, or 8192 bytes.

The maximum amount of time to wait for a response from the PLC, in milliseconds.  Time is counted starting from when the function is called by the application.  It is important to set this variable to a high value (such as 3 seconds) when working with large data sets, as PLCIO may need to split the data set into multiple transactions -- each taking several tenths of a second to complete.

If the timeout is reached, these functions will return -1 with the resulting error code set to PLCE_TIMEOUT.  Even with an error, it is possible that the PLC has already completed the request, or it can still complete the request sometime in the future if the communications channel is slow.  Applications should simply retry the request again in this situation, and they need not use plc_close( ).

Use 0 to disable this timeout on a request-by-request basis.

char *pc_format

Instructs PLCIO on how to convert the data being read or written between PLC and application.  This string is only used when the byte-order differs between the CPU type used by the PLC and that of the application.

There are several modes of operation for this argument:

  • If set to NULL (or PLC_CVT_WORD), PLCIO assumes the data consists solely of 16-bit short integers.  This default handles most PLC communications.

  • If set to PLC_CVT_NONE, no translation is performed.

  • If set to a string, the string describes the size and number of bytes of each different set of data members in the p_data buffer.

Creation of the string requires knowledge of the data buffer members in question.  The string consists of a set of characters of the form "<type_id>[optional length in bytes]", where type_id is one of the following:

c

char

Character or Byte Data (8-bit)

i

short

Short Integer (16-bit)

j

int

Integer (32-bit for UNIX computers)

q

quad

Long-Long Integer (64-bit on UNIX computers)

r

float

Floating point (32-bit)

d

double

Double Precision Floating Point (64-bit)

Conversion Example:
For the packed structure in the following format:

int a[5];       /*   5 int * 4 bytes = 20 bytes      */
char b[10];  /*   10 char = 10 bytes                */
short c[3];   /*   3 short * 2 bytes = 6 bytes    */
int d;          /*   1, 4 byte integer = 4 bytes     */
short e;      /*   1, 2 byte short = 2 bytes        */

Define the pc_format string as: "j20c10i6j4i2".

 
 

Return Value

Function plc_read( ) returns the length in bytes of the data read into the p_data buffer from the PLC.  0 indicates that the operation completed but no data was received.  Returns -1 on error.

Function plc_write( ) returns 0 if the operation completed successfully, or -1 if an error has occurred.

 

General Errors

PLCE_NULL

Function called with NULL PLC pointer.

PLCE_NO_SUPPORT

Function not supported by this PLC module.

PLCE_INVALID_MODE

plc_write( ) called when PLC is being accessed in slave mode.

PLCE_INVALID_OP

Operation j_op not valid for this function.  For instance, using PLC_RREG during a plc_write( ), and vice versa.

PLCE_INVALID_POINT

Function called with NULL or empty pc_addr string, and PLC is being accessed in master mode.

PLCE_BAD_SOFTPOINT

Address pc_addr was not found in the Soft Point-configuration file.

PLCE_INVALID_LENGTH

Function called with invalid j_length (less than 1 or greater than PLC_CHAR_MAX).

PLCE_NO_READ

Application used plc_read( ) to read from a point not marked R in the Soft Point-configuration file.

PLCE_NO_WRITE

Application used plc_write( ) to write to a point not marked W in the Soft Point-configuration file.

PLCE_CONV_FORMAT

Unknown character found in conversion string pc_format.

PLCE_PARSE_ADDRESS

Could not identify PLC target data location due to error in pc_addr semantics (misplaced parentheses, brackets, etc).

PLCE_BAD_ADDRESS

Specified address does not physically exist on the PLC.

PLCE_REQ_TOO_LARGE

Target address data size is too small for the specified j_length.

PLCE_BAD_REQUEST

Size j_length is not evenly divisible by the element size requested in j_op.

PLCE_ACCESS_DENIED Remote read/write access is disallowed for this specific address.

PLCE_INVALID_DATA

The data values sent in a plc_write( ) operation were rejected by the PLC due to data type restrictions on the target address.

PLCE_COMM_SEND

A transport error occurred while sending the read/write request to the PLC.  The UNIX errno is stored in
plc_ptr->j_errno.

PLCE_COMM_RECV

A transport error occurred while waiting for the response from the PLC.  The UNIX errno is stored in
plc_ptr->j_errno

PLCE_TIMEOUT

No response was received after j_timeout milliseconds have elapsed.  plc_ptr->j_errno is set to ETIMEDOUT.

PLCE_MSG_TRUNC

Received a response from the PLC too small to process.

 
 

Application Example

Below is a typical example of a communication using PLCIO.  This function reads ten 16-bit registers (PLC_RREG operation) from an address named “Pdata” on a generic PLC (see Soft Point Configuration file example), and saves the data to the ai_regs argument.  It sets the transaction timeout to 3 seconds and uses PLC_CVT_WORD, telling PLCIO that the buffer contains only 16-bit words for byte-order conversion.

int read_from_plc(PLC *plc_ptr, short ai_regs[10])
{
  int j_length;

  j_length=plc_read(plc_ptr, PLC_RREG, "Pdata", ai_regs, 20, 3000, PLC_CVT_WORD);
  if(j_length == 20)
    return 1; /* Completed successfully */

  /* Otherwise, there's been a problem */
  if(j_length == -1)
    plc_print_error(ptr, "read_from_plc");
  else
    printf("Only received %d bytes from the PLC.", j_length);

  return 0; /* Operation failed */
}

 
 

The following code snippet shows typical usage of the read_from_plc( ) function above. If the request fails, we check the j_error member of the PLC object to determine what action is necessary.  Only return codes PLCE_COMM_SEND and PLCE_COMM_RECV are considered fatal communications errors, and the application should close and reopen the PLC to correct the problem.  If PLCE_TIMEOUT is received, the application should simply retry the request.  All other errors, such as an invalid PLC address, indicate a problem with PLCIO or the application, rather than the communications link.

...
j_status=read_from_plc(plc_ptr, ai_regs);
if(j_status == 0) {
  /* An error has occurred with the PLC request */
  if(plc_ptr->j_error == PLCE_COMM_SEND || plc_ptr->j_error == PLCE_COMM_RECV) {

    /* Communications error; close and reopen PLC */
    plc_close(plc_ptr);
    plc_ptr=plc_open(PLC_OPEN_STRING);
    if(!plc_ptr) {
      plc_print_error(plc_ptr, "Reopening PLC");
      exit(1); /* Unrecoverable error */
    }
  } else if(plc_ptr->j_error != PLCE_TIMEOUT) {
    /* A non-timeout has occurred; something is wrong with the program */
    exit(1);
  }
}
...

See plc_error( ) for more information on checking the returned error code of a read/write request.

* Note: The plc_write( ) function can not be used in slave PLC mode (unsolicited communication).  Use plc_receive( ) and plc_reply( ) instead.