Help with getting a PLC interfaced

  • Koolguy007
    17th January Member 0 Permalink

    So I have a pretty unique project I'm attempting. I have an industrial PLC that I would like to interface with TPT. The controller communicates over a TCP socket using a proprietary communication protocol that I am very familiar with. So far I've made a lua script that when loaded into TPT will set and read bits. My issue lies with trying to get my head around how to interface this with the simulation. I tried making an element that just sets a bit if sparked and turns it off when not being sparked. I didn't figure that would work due to the speed at which the game is ticking vs how fast the PLC can send packets, and it did break. Ultimately I'd like to have bits in the PLC trigger sparks, sparks trigger bits in the PLC, and have temperatures and pressures be written to registers.

     

    Edit: Apparently I just needed to sleep on it. I figured it out by using the tick event to make a basic polling timer. I have an element the successfuly triggers a bit in my PLC while being sparked. Now I just have to figure out how to scale it up for dozens of bits. I've seen where some peope use the temp varibles to set attributes, so probably that.

    Edited once by Koolguy007. Last: 19th January
  • Koolguy007
    21st January Member 0 Permalink

    So, I have a very rough version of my interface script working which includes alot of copy paste code from the breakpoint mod and the logic gates mod. To the authors of those mods I am very grateful and sorry. Anyways, I'm just going to toss my script in here just incase it someone else would like to interface something extrenal to TPT and needs some ideas.

     

    local host, port = "192.168.1.1", 1026
    local socket = rawget(_G, "socket")
    local tcp = assert(socket.tcp())
    local updateTime = 15
    local xBits = '\x00\x00\x00\x00'
    local yBits = '\x00\x00\x00\x00'
    local data = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'


    tcp:settimeout(1, 't')
    tcp:connect(host, port)

    --This handles talking to the PLC--
    event.register(event.tick, function()
      --This bit gets 2 words from the PLC starting at Y120
      if updateTime == 10 then
        buff = '\x00\x00\x05\x00\x1c\x12\x01\x02\x00'
        tcp:send(buff)
        local buff = tcp:receive(9)
        yBits = string.sub(buff, -4)
      elseif updateTime == 5 then
      --This bit sets 2 words worth of bits in the PLC starting at X100
        buff = '\x00\x00\x07\x00\x1d\x10\x01'..xBits
        tcp:send(buff)
        buff = tcp:receive(5)
        xBits = '\x00\x00\x00\x00'
      elseif updateTime == 0 then
      --This bit sends the data variable to the PLC starting at D100
        buff = '\x00\x00\x13\x00\x1d\x10\x10' .. data
        tcp:send(buff)
        buff = tcp:receive(5)
        data = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
        updateTime = 15
      end
      updateTime = updateTime - 1
    end)

    --This just replaces a section in a string with another string--
    function replace_char(i, str, r)
      i = i + 1
      return str:sub(1, i-1) .. r .. str:sub(i+string.len(r))
    end

    --This turns on a bit at index "i" in the xBits variable--
    function setX(i)
      if i > 23 then
        i = i - 24
        i = bit.lshift(1, i)
        local index = 4
        local x = string.char(bit.bor(string.byte(string.sub(xBits, index, index)), i))
        xBits = replace_char(index-1, xBits, x)
      elseif i > 15 then
        i = i - 16
        i = bit.lshift(1, i)
        index = 3
        x = string.char(bit.bor(string.byte(string.sub(xBits, index, index)), i))
        xBits = replace_char(index-1, xBits, x)
      elseif i > 7 then
        i = i - 8
        i = bit.lshift(1, i)
        index = 2
        x = string.char(bit.bor(string.byte(string.sub(xBits, index, index)), i))
        xBits = replace_char(index-1, xBits, x)
      else
        i = bit.lshift(1, i)
        index = 1
        x = string.char(bit.bor(string.byte(string.sub(xBits, index, index)), i))
        xBits = replace_char(index-1, xBits, x)
      end
    end

    --This returns a bit from index "i" in the yBits variable--
    function readY(i)
      if i > 23 then
      i = i - 24
      x = string.byte(string.sub(yBits, 4, 4))
      x = bit.rshift(x, i)
      x = bit.band(x, 1)
      elseif i > 15 then
      i = i - 16
      x = string.byte(string.sub(yBits, 3, 3))
      x = bit.rshift(x, i)
      x = bit.band(x, 1)
      elseif i > 7 then
      i = i - 8
      x = string.byte(string.sub(yBits, 2, 2))
      x = bit.rshift(x, i)
      x = bit.band(x, 1)
      else
      x = string.byte(string.sub(yBits, 1, 1))
      x = bit.rshift(x, i)
      x = bit.band(x, 1)
      end
      return x
    end

    function setData(i, temp, press)
      temp = math.floor(temp)
      local x = bit.band(temp, 255)
      local y = bit.band(bit.rshift(temp, 8), 255)
      temp = string.char(x) .. string.char(y)
      press = math.floor(press)
      x = bit.band(press, 255)
      y = bit.band(bit.rshift(press, 8), 255)
      press = string.char(x) .. string.char(y)
      local str = press .. temp
      data = replace_char(i * 4, data, str)
    end

    --This starts the setbit element--
    local el_tpsb = elem.allocate("Koolguy", "TPSB")
    elem.element(el_tpsb, elem.element(elem.DEFAULT_PT_DMND))
    elem.property(el_tpsb, "Name", "TPSB")
    elem.property(el_tpsb, "Colour", 0xDEADBEEF)
    elem.property(el_tpsb, "Description", "Sets a bit in the PLC when sparked.")
    elem.property(el_tpsb, "MenuSection", elem.SC_SENSOR)
    elem.property(el_tpsb, "Properties", elem.PROP_LIFE_DEC)
    elem.property(el_tpsb, "Temperature", 273.15)
    elem.property(el_tpsb, "HeatConduct", 0)
    elem.property(el_tpsb, "Update", function (i, x, y, ss, nt)
        local life = sim.partProperty(i, "life")
        for ry=-1,1,1 do
          for rx=-1,1,1 do
            if life == 0 and tpt.get_property("type", x+rx, y+ry) == 15 then
              local index = sim.partProperty(i, "temp") - 273.15
              setX(index)
              sim.partProperty(i, "life", 10)
            end
          end
        end
      end)

    elem.property(el_tpsb, "Graphics", function (i, colr, colg, colb)
      local x, y = sim.partPosition(i)
      local life = sim.partProperty(i, "life")
        if life > 0 then
          pixel_mode = 296
          colg, colb = 0, 0
          colr = 255
        else
          pixel_mode = 1
        end
        return 0,pixel_mode,255,colr,colg,colb,255,colr,colg,colb
      end)
    --This ends the setbit element--

    --This starts the readbit element--
    local el_tprb = elem.allocate("Koolguy", "TPRB")
    elem.element(el_tprb, elem.element(elem.DEFAULT_PT_DMND))
    elem.property(el_tprb, "Name", "TPRB")
    elem.property(el_tprb, "Colour", 0xDEADBEEF)
    elem.property(el_tprb, "Description", "Reads a bit from the PLC and sparks if on.")
    elem.property(el_tprb, "MenuSection", elem.SC_SENSOR)
    elem.property(el_tprb, "Properties", elem.PROP_LIFE_DEC)
    elem.property(el_tprb, "Temperature", 273.15)
    elem.property(el_tprb, "HeatConduct", 0)
    elem.property(el_tprb, "Update", function (i, x, y, ss, nt)
      for dx = -1, 1 do
        for dy = -1, 1 do
          local index = sim.partProperty(i, "temp") - 273.15
          yOn = readY(index)
          if yOn == 1 then
            sim.partCreate(-1, x + dx, y + dy, elem.DEFAULT_PT_SPRK)
          end
        end
      end
    end)

    elem.property(el_tprb, "Graphics", function (i, colr, colg, colb)
      local x, y = sim.partPosition(i)
        if yOn == 1 then
          pixel_mode = 296
          colr, colg, colb = 0, 255, 64
        else
          pixel_mode = 1
        end
        return 0,pixel_mode,255,colr,colg,colb,255,colr,colg,colb
      end)
    --This ends the readbit element--

    --This starts the pressure and temperature sensor element--
    local el_tpsb = elem.allocate("Koolguy", "TPTP")
    elem.element(el_tpsb, elem.element(elem.DEFAULT_PT_DMND))
    elem.property(el_tpsb, "Name", "TPTP")
    elem.property(el_tpsb, "Colour", 0xDEADBEEF)
    elem.property(el_tpsb, "Description", "Sends temp and press to the PLC in pairs of bytes.")
    elem.property(el_tpsb, "MenuSection", elem.SC_SENSOR)
    elem.property(el_tpsb, "Properties", elem.PROP_LIFE_DEC)
    elem.property(el_tpsb, "HeatConduct", 255)
    elem.property(el_tpsb, "Update", function (i, x, y, ss, nt)
      setData(sim.partProperty(i, "tmp"), sim.partProperty(i, "temp"), sim.pressure(x/4, y/4))
      end)
    --This ends the pressure and temperature sensor element--