Skip to content

Simulating a PLC #73

Open
Open

Description

@piertoni

Hi, I would like to simulate a PLC.
In my case when I read an address, let's say 10 words starting from 0x200, I get some values back (I.E. a string containing the Producer), instead when I read 10 words from 0x201 I will get completely different values.
Which is the proper way to realize this? For what I have understood with app.route I can only define one value at a time. Is there a way to get the lenght of the request at application level?

The workaround that came to my mind was to trigger the modification of regs_word when the first register is requested, like:

    @app.route(slave_ids=[1],function_codes=[3], addresses=(0x200,1) ):
    def producer(slave_id, function_code, address):
        regs_word[0x200] = 0xaabb # first registers
        regs_word[0x201] = 0x2231 # prepares other registers
        regs_word[0x202] = 0x231a # prepares other registers
        # ...
        return regs_word[0x200]

    @app.route(slave_ids=[1],function_codes=[3], addresses=(0x201,15) ):
    def other_regs(slave_id, function_code, address):
        return regs_word[address]

Thanks

Activity

  1. OrangeTux commented on Aug 27, 2019

    @OrangeTux
    Collaborator

    Hello @piertoni,

    Thanks for your question. From what I understand you want the value of a register to be depending on the start address of the request. Am I correct?

    For what I have understood with app.route I can only define one value at a time.

    It is correct that the the handlers should return only 1 value. If a client requests multiple registers than the handler is executed multiple times using a different value for each address.

    But wouldn't something like this work for your?

    @app.route(slave_ids=[1],function_codes=[3], addresses=list(range(0x200,0x215))):
        def producer(slave_id, function_code, address):
            if address == 0x200:
                # prepare all your other registers
    
                regs_word[0x200] = 0xaabb # first registers
                regs_word[0x201] = 0x2231 # prepares other registers
                regs_word[0x202] = 0x231a # prepares other registers
          
         return regs_word[0x200]
  2. piertoni commented on Aug 27, 2019

    @piertoni
    Author

    Hello @piertoni,

    Thanks for your question. From what I understand you want the value of a register to be depending on the start address of the request. Am I correct?

    Exactly, in fact I am replicating the PLC behavior and if I read 16 regs starting from 0x200 I get one string I.E. Producer name, If I read 16 regs starting from 0x201 I get another string I.E. production date. Is not really a standard modbus behavior from my point of view... but is like this 😄

    For what I have understood with app.route I can only define one value at a time.

    It is correct that the the handlers should return only 1 value. If a client requests multiple registers than the handler is executed multiple times using a different value for each address.

    But wouldn't something like this work for your?

    @app.route(slave_ids=[1],function_codes=[3], addresses=list(range(0x200,0x215))):
        def producer(slave_id, function_code, address):
            if address == 0x200:
                # prepare all your other registers
    
                regs_word[0x200] = 0xaabb # first registers
                regs_word[0x201] = 0x2231 # prepares other registers
                regs_word[0x202] = 0x231a # prepares other registers
          
         return regs_word[0x200]

    Yes, this will work better than my code! Thanks!
    It's a hack anyway but for a simulator I think is fine.
    Really thanks!

  3. piertoni commented on Aug 27, 2019

    @piertoni
    Author

    mmm thinking better I don't know if this (in general) will work because should be something like this:

        @app.route(slave_ids=[1],function_codes=[3], addresses=list(range(0x200,0x215))):
        def producer(slave_id, function_code, address):
            if address == 0x200:
                # prepare all your other registers
    
                regs_word[0x200] = 0xaabb # first registers
                regs_word[0x201] = 0x2231 # prepares other registers
                regs_word[0x202] = 0x231a # prepares other registers
          
         return regs_word[address]
    
        @app.route(slave_ids=[1],function_codes=[3], addresses=list(range(0x201,0x216))):
        def production_date(slave_id, function_code, address):
            if address == 0x201:
                # prepare all your other registers
    
                regs_word[0x200] = 0x1111 # first registers
                regs_word[0x201] = 0x0000 # prepares other registers
                regs_word[0x202] = 0x123b # prepares other registers
          
         return regs_word[address]

    In fact the region are overlapping... I don't think will work, what do you think?

  4. OrangeTux commented on Aug 27, 2019

    @OrangeTux
    Collaborator

    It's a hack anyway but for a simulator I think is fine.

    I am not sure if there is a non-hacky option. To be fair, your requirement of having registers values depend on the start address seem a little bit odd for me.

    In fact the region are overlapping... I don't think will work, what do you think?

    I am not exactly how this will work out, but I don't think it will work as you want it to work. At least uModbus is not intended to work that way when I created this package.

  5. piertoni commented on Aug 28, 2019

    @piertoni
    Author

    I am not sure if there is a non-hacky option. To be fair, your requirement of having registers values depend on the start address seem a little bit odd for me.

    Yeah, I agree, that seems odd to me too... but ask Wago producers... 😄

    In fact the region are overlapping... I don't think will work, what do you think?

    I am not exactly how this will work out, but I don't think it will work as you want it to work. At least uModbus is not intended to work that way when I created this package.

    If you think that it is possible to retrieve the length of the request just tell me where to start and eventually I can write the code.
    If not I will close the issue, thank you for uModbus, is cool! 😄

  6. OrangeTux commented on Aug 29, 2019

    @OrangeTux
    Collaborator

    If you think that it is possible to retrieve the length of the request just tell me where to start and eventually I can write the code.

    Assuming you run a Modbus TCP server than you could create your own request handler that extends from the umodbus.server.tcp.RequestHandler. If you than override than the umodbus.server.AbstractRequestHandler.execute_route() you have access to the info you want.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions