python-snap7 icon indicating copy to clipboard operation
python-snap7 copied to clipboard

WriteArea() function

Open gustaf71 opened this issue 9 months ago • 7 comments

Hi.

I had a chat with the actor of Snap7 and the function WriteArea() When he was looking at the source code of the function for python-snap7 he couldn't understand why the WordLen was not handled in correct way.

In other words WordLen.Byte is not the only way to use WriteArea().

If you like to do

byte_index*8+bit_index with size of 1, that is not possible if word_len is not WordLen.Bit

For a better understanding what I'm talking about is to compare the orginal source code of Snap7 with the implementation of the same function in python-snap7

Or

Get the library Settimino for Arduino and check the source code.

If I was a good programmer I maybe will do it myself. But I like to hear from the people who made python-snap7 why the WriteArea() is different ??

gustaf71 avatar Mar 28 '25 16:03 gustaf71

If siemens was a good software company they would opensource their libary and plc interfaces instead of stealing open source solution and change it a tiny tiny bit and publish it closed source. So some software engineers did had to write this library in a huge hurry. So it Could very well be a mistake @gustaf71 ...

spreeker avatar Mar 28 '25 16:03 spreeker

Snap7 have been included in OpenPLC_v3 runtime a open source SoftPLC. So Snap7 is still alive and I'm sure still very useful. That said its difficult to use something that do not adapt to the original approach. Specially when the acthor of Snap7 have made a execelent documentation of Cli_WriteArea() where a custom function could be made ex. WriteBit() and use S7WLBit as WordLen.

Do you know if python-snap7 is still active ?

gustaf71 avatar Mar 29 '25 08:03 gustaf71

Create a pull request?

spreeker avatar Mar 29 '25 09:03 spreeker

Here is example of write_bit() function that use Cli_WriteArea() function. As you could see the WordLen use S7WLBit or 0x01 and not 0x02

 def write_bit(self, area_type, db_number, byte_index, bit_index, bit_value):
        """
        Write a specific bit value to a PLC
        """
        self.snap7.Cli_WriteArea.argtypes = [
            ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.c_int,
            ctypes.c_int, ctypes.c_int, ctypes.c_void_p
        ]
        self.snap7.Cli_WriteArea.restype = ctypes.c_int

        if bit_index < 0 or bit_index > 7:
            raise ValueError("bit_index must be between 0 and 7")

        bit_value = ctypes.c_uint8(bit_value)

        res = self.snap7.Cli_WriteArea(
            self.client, area_type, db_number, byte_index*8+bit_index,
            1, S7WLBit, ctypes.byref(bit_value)
        )

        if res == 0:
            print("Write success!")
            return res
        else:
            error_text = ctypes.create_string_buffer(1024)
            self.snap7.Cli_ErrorText(res, error_text, 1024)
            raise Exception(f"Failed to write area. Error: {error_text.value.decode()}")

gustaf71 avatar Apr 08 '25 10:04 gustaf71

@gustaf71 Can you please add some tests to your code and create a merge request ? This will be very helpfull for the project.

We are working on your free time on the project, we do our best !

lupaulus avatar Apr 18 '25 20:04 lupaulus

The code is based on my own ctypes implementation, python-snap7 use ctypes but the WriteArea() function is hardcoded to use S7WLByte this is (0x02). The problem with that approach is if we like to write bit we need to use S7WLBit (0x01) as word length. The implementation of WriteArea() is not follow the snap7 implementation.

I'm not directly a software developer but I have done coding using snap7 both for C and Python.

The best approach is to follow the snap7 implementation of WriteArea function. The code have been tested on s7-300 and Siemens LOGO.

gustaf71 avatar Apr 23 '25 16:04 gustaf71

looks like you are missing a way to pass the wordlen to the write_area() function. I think the code belows would solve your problem in a backwards compatible way, let me know what you think:

    def write_area(self, area: Area, db_number: int, start: int,
                             data: bytearray, word_len: Optional[WordLen] = None) -> int:
        if not word_len:
            if area == Area.TM:
                word_len = WordLen.Timer
            elif area == Area.CT:
                word_len = WordLen.Counter
            else:
                word_len = WordLen.Byte
                
        type_ = WordLen.Byte.ctype
        size = len(data)
        logger.debug(
            f"writing area: {area.name} db_number: {db_number} start: {start}: size {size}: "
            f"word_len {word_len.name}={word_len} type: {type_}"
        )
        cdata = (type_ * len(data)).from_buffer_copy(data)
        return self._lib.Cli_WriteArea(self._s7_client, area, db_number, start, size, word_len, byref(cdata))

gijzelaerr avatar Jul 03 '25 11:07 gijzelaerr