Logic Machine Forum
Again about semaphore - Printable Version

+- Logic Machine Forum (https://forum.logicmachine.net)
+-- Forum: LogicMachine eco-system (https://forum.logicmachine.net/forumdisplay.php?fid=1)
+--- Forum: Scripting (https://forum.logicmachine.net/forumdisplay.php?fid=8)
+--- Thread: Again about semaphore (/showthread.php?tid=3008)



Again about semaphore - Andey - 23.11.2020

Hello.
In my home I have FN485 devices. They are controlled via dedicated RS485 bus. FN485 can to operate as stand alone device - it has input and output, when input is closed output relay is closed too. But I decided that I need additional control. To turn light from application, to know what inputs are closed, to turn light when switch that is controlled by other device is toggled. So I see at least three events that can to access to RS485 bus to operate with FN485. And I decided that I need to manage access to serial bus. The only way I know is to use semaphore. In scripting tab/user library  I have created script you can to see below
Code:
require('sem')
require('serial')

function readSerialFN485()
  local buf = {}
  while true do
    local timeout = #buf > 0 and 0.5 or 15
    local char = port:read(1, timeout)
   
    if char then
      buf[ #buf + 1 ] = char:byte()
    else
      return buf
    end
  end
end


function FN485_write(param1, relay, value)

  local semaphore = sem.open("FN485_serial_access")
  if semaphore:trywait() == false then
    semaphore:wait()
  end
 
  if not port then
    port = serial.open('/dev/RS485-1', { baudrate = 9600, duplex = 'half' })
    port:flush()
  end
 
  data_addr = 100 + param1
  data_relay = 100 + relay
  data_value = 100
  if value then
    data_value = 100 + 1
  end
 
  qqq = string.format('%c%c%c%c%c%c%c%c%c%c', 99, data_addr, data_addr, 211, 211, data_relay, data_relay, data_value, data_value, 13)
  if port then
    port:write(qqq)
    port:drain()
  end
 
  semaphore:post()
 
end
-- function reads all input states
-- returns bitwise UINT8 value; bit 0 = state of input 0
function FN485_read_all_inputs(param1)
  local semaphore = sem.open("FN485_serial_access")
  if semaphore:trywait() == false then
    semaphore:wait()
  end
 
  if not port then
    port = serial.open('/dev/RS485-1', { baudrate = 9600, duplex = 'half' })
    port:flush()
  end
 
  data_addr = 100 + param1
 
  qqq = string.format('%c%c%c%c%c%c%c%c%c%c', 99, data_addr, data_addr, 109, 109, 100, 100, 100, 100, 13)
  if port then
    port:write(qqq)
    port:drain()
   
    res = readSerialFN485()
   
  end
 
 
  -- parse read result
  if res then
    -- check read parameters and if parameters are OK return something
    if (res[2] == data_addr and res[3] == data_addr and res[4] == 109 and res[5] == 109) then
      local retValue = bit.lshift( res[7] - 100, 4) + res[9] - 100
      return retValue
    else
      log(res)
    end
  end
end

From point of view code is correct. 
For test purposes I have started to call read all input function from resident script with log output each time after read_all_inputs function is called. And I see that log is properly created only several times. Then I need to open resident script and save it to get again only several logs. But had expected continuous logs with timeout I had specified for resident script.
 What I am doing wrong.


RE: Again about semaphore - admin - 24.11.2020

Restarting the script during execution can break the semaphore status. My suggestion is to use only one resident script that combines RS485 and localbus for monitoring certain objects and writing to the port when values change.
You can also use string.char instead of string.format so you don't have to worry about the format string having correct number of elements.


RE: Again about semaphore - Andey - 24.11.2020

Hello
I understand that single resident script is better practice. And semaphore is not necessary in this case. But there are some other events (not only timer to start resident script) that can to cause access to RS485 bus. These events can be object change by modbus profile mapping engine or object change from visualization. So I see potential parallel access to RS485.
Now resident script is a test for semaphore usage. And I see that it logs output message continuously with proper timeout when semaphore is not used (commented) and only several logs when semaphore is used.
I think some script debug tips (set breakpoint, steps running) can to help me to describe the problem in more details. But I do not know does debug is available at all for scripts. Can you to suggest me something to implement and report you more information?

Thanks for tip with string.char method usage.