Applies To:
  • CitectSCADA 5.5x
  • CitectHMI 5.5x

Summary:
I have a customer that is used to using Wonderware, and they wondered if we have a similar function. They have what they call "dot modifiers", or similar. These allow you to take an existing tag (16 bit integer, for example), and look at an individual bit without using a separate tag.

For example:

Allen Bradley - N7:20 is the integer.

start bit = n7:20.1
stop bit = N7:20.2
Running bit = N7:20.3

To do this, I can use the BITAND function to pick a bit, but how can I toggle, or set a bit in the word, without changing the entire word, or using a separate tag for each? I want to toggle just bit 1, while leaving the rest of the tag alone. In Wonderware, this would require one tag. For example: pump1, and the three tags would be as follows:

start = Pump1.1
stop = Pump1.2
Running = Pump1.3

Is there something that we can do similarly, without having to use multiple tags?

 

Solution:
It is possible to do what you are suggesting but there are a number of caveats.

The recommended method where a bit is being written too, is to use individual tags for each bit (i.e. address them to the bit level such as start bit = n7:20/1). I understand customers may wish to do it the other way to minimise the tag count. Indeed even when bits are only being read it is good practice to use individual tags for each as this results in a more maintainable system (despite increasing the tag count).

To read a bit from a word the method is to use BITAND as you have described. The only drawback with this method is the system becomes less maintainable (i.e. its not exactly self documenting when you have to refer to some external table to see that the function of bit 3 is "running" for example). Technically however this method is perfectly valid.

To write a bit from a word is a three step process known as read-modify-write (there is no standard function in citect for this, i.e. you will have to write your own cicode function, an example is provided below). First read the word into a temporary variable. Then modify just the bit you are interested in using BITOR for example. Then write the temporary variable (the whole word) back to the tag. The drawback of this method is that there is a possibility that in between reading and writing back, the other bits in the source tag may change and you could end up overwriting them with what they used to be. Depending on how your system is configured this may or may not be a concern.

The reasons for this are that if you configure a tag for an entire word then the protocol driver is then sending messages to the PLC to read/write entire words. Thus you need to use the above method and consider the potential issues. However if you configure a tag for an individual bit then the protocol driver is sending the messages to read/write individual bits (i.e. without affecting the adjacent bits in the same word).

I would expect that the Wonderware system works the same way (i.e. with the same drawbacks) however I may be wrong in this. For a system to work differently would require very tight integration with the particular protocol and this would not be typical of a system designed to work with controller hardware from multiple vendors, such as Wonderware or Citect (as opposed to [for example] RSView, which may or may not have the same issues).

Note: some protocol drivers in Citect use the read-modify-write method to write bits since the protocols may not natively support bit writes. Refer to KB Q1445 for a list of protocols affected.

Example functions:

// Bits.ci
// Functions for reading and writing individual bits in INT or LONG variables or Cicode variables
//
// Note: BitWrite and BitToggle do not write to the original tag. You must copy the return value
// into the desired tag. This allows Citect to use its own blocking to improve performance,
// since TagRead() and TagWrite() aren't used.

//

// Write TRUE or FALSE (0 or 1) to any bit (0-31) in an integer
//
// Example:
// To set bit 31 in Tag1 to 0, use the following command:
// Tag1 = BitWrite(Tag1, 31, 0)
//

INT
FUNCTION

BitWrite(
INT iValue, INT iBitno, INT bState)

IF
bState = FALSE THEN
    RETURN
iValue BITAND (4294967295 BITXOR Pow(2, iBitNo));
ELSE
    RETURN
iValue BITOR Pow(2, iBitNo);
END
END

// Toggle any bit (0-31) in an integer
//
// Example:
// To toggle bit 16 in Tag1, use the following command:
// Tag1 = BitToggle(Tag1, 16)
//

INT
FUNCTION

BitToggle(
INT iValue, INT iBitNo)
    RETURN
iValue BITXOR Pow(2, iBitNo);
END


// Read an indivual bit number (0-31) from an integer and return
// the state (0 or 1)

INT
FUNCTION

BitRead(
INT iValue, INT iBitNo)
    RETURN
(iValue BITAND Pow(2, iBitNo)) <> 0;
END

 

Keywords:
 

Attachments