frames 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. #!/usr/bin/ruby
  2. #
  3. # This file is part of centurio.work/ing/commands.
  4. #
  5. # centurio.work/ing/commands is free software: you can redistribute it and/or
  6. # modify it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or (at your
  8. # option) any later version.
  9. #
  10. # centurio.work/ing/commands is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
  13. # Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License along with
  16. # centurio.work/ing/commands (file COPYING in the main directory). If not, see
  17. # <http://www.gnu.org/licenses/>.
  18. require 'rubygems'
  19. require 'json'
  20. require 'xml/smart'
  21. require 'riddl/server'
  22. require 'fileutils'
  23. require 'typhoeus'
  24. # process:
  25. # https://centurio.work/customers/evva/flow/?monitor=https://centurio.work/flow-test/engine/729/
  26. class Get < Riddl::Implementation
  27. def response
  28. if @r[0] == 'test'
  29. Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','test.html')))
  30. elsif @r[0] == 'menu'
  31. Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','menu.html')))
  32. elsif @r[0] == 'framedata'
  33. Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','framedata.html')))
  34. else
  35. Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','template.html')))
  36. end
  37. end
  38. end
  39. class InitFrame < Riddl::Implementation
  40. def response
  41. Dir.mkdir(File.join('data',@r.last)) rescue nil
  42. if @p[0].value.to_s.strip.empty?
  43. File.write(File.join('data',@r.last,'style.url'),@p[0].value)
  44. end
  45. File.write(File.join('data',@r.last,'frames.json'),JSON.dump(JSON.parse('{"data":[]}')))
  46. #for handler
  47. File.write(File.join('data',@r.last,'dataelements.json'),JSON.dump(JSON.parse('{"data":[]}')))
  48. File.write(File.join('data',@r.last,'info.json'),JSON.dump(JSON.parse('{"x_amount":' + @p[2].value + ', "y_amount":' + @p[3].value + ', "lang":"' + @p[4].value + '", "langs":["' + @p[4].value + '"]}')))
  49. File.write(File.join('data',@r.last,'callback'),@h['CPEE_CALLBACK'])
  50. File.write(File.join('data',@r.last,'cpeeinstance.url'),@h['CPEE_INSTANCE_URL'])
  51. @a[0].send('new')
  52. nil
  53. end
  54. #def headers
  55. # Riddl::Header.new('CPEE-CALLBACK', 'true')
  56. #end
  57. end
  58. #https://coderwall.com/p/atyfyq/ruby-string-to-boolean
  59. #showbutton
  60. class String
  61. def to_bool
  62. return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
  63. return false if self == false || self.empty? || self =~ (/(false|f|no|n|0)$/i)
  64. raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
  65. end
  66. end
  67. class NewFrameSet < Riddl::Implementation
  68. def response
  69. path = File.join('data',@r.last,'frames.json')
  70. file = File.read(path)
  71. data_hash = JSON::parse(file)
  72. #check if new frame overlaps others if it does, delete overlapped frames
  73. data_hash["data"].each do | c |
  74. if doOverlap(c['lx'], c['ly'], c['rx'], c['ry'], @p[1].value.to_i, @p[2].value.to_i, (@p[1].value.to_i + @p[3].value.to_i - 1), (@p[2].value.to_i + @p[4].value.to_i - 1))
  75. data_hash["data"].delete(c)
  76. end
  77. end
  78. urls = JSON::parse(@p[6].value);
  79. if @p[7].value == ""
  80. hash = {lx: @p[1].value.to_i, ly: @p[2].value.to_i, rx: (@p[1].value.to_i + @p[3].value.to_i - 1), ry: (@p[2].value.to_i + @p[4].value.to_i - 1), url: urls, showbutton: @p[5].value, default: "{}", callback: @h['CPEE_CALLBACK']};
  81. else
  82. hash = {lx: @p[1].value.to_i, ly: @p[2].value.to_i, rx: (@p[1].value.to_i + @p[3].value.to_i - 1), ry: (@p[2].value.to_i + @p[4].value.to_i - 1), url: urls, showbutton: @p[5].value, default: JSON::parse(@p[7].value), callback: @h['CPEE_CALLBACK']};
  83. end
  84. data_hash["data"].push(hash)
  85. File.write(path, JSON.dump(data_hash))
  86. #only send active url to client
  87. infofile = File.join('data',@r.last,'info.json')
  88. infojson = JSON::parse(File.read(infofile))
  89. hash["url"] = urls.find{ |h| h['lang'] == infojson["lang"]}['url']
  90. @a[0].send(JSON.dump(hash))
  91. nil
  92. end
  93. end
  94. class NewFrameWait < Riddl::Implementation
  95. def response
  96. path = File.join('data',@r.last,'frames.json')
  97. file = File.read(path)
  98. data_hash = JSON::parse(file)
  99. #check if new frame overlaps others if it does, delete overlapped frames
  100. data_hash["data"].each do | c |
  101. if doOverlap(c['lx'], c['ly'], c['rx'], c['ry'], @p[1].value.to_i, @p[2].value.to_i, (@p[1].value.to_i + @p[3].value.to_i - 1), (@p[2].value.to_i + @p[4].value.to_i - 1))
  102. data_hash["data"].delete(c)
  103. end
  104. end
  105. urls = JSON::parse(@p[6].value);
  106. if @p[7].value == ""
  107. hash = {lx: @p[1].value.to_i, ly: @p[2].value.to_i, rx: (@p[1].value.to_i + @p[3].value.to_i - 1), ry: (@p[2].value.to_i + @p[4].value.to_i - 1), url: urls, showbutton: @p[5].value, default: "{}", callback: @h['CPEE_CALLBACK']};
  108. else
  109. hash = {lx: @p[1].value.to_i, ly: @p[2].value.to_i, rx: (@p[1].value.to_i + @p[3].value.to_i - 1), ry: (@p[2].value.to_i + @p[4].value.to_i - 1), url: urls, showbutton: @p[5].value, default: JSON::parse(@p[7].value), callback: @h['CPEE_CALLBACK']};
  110. end
  111. data_hash["data"].push(hash)
  112. File.write(path, JSON.dump(data_hash))
  113. #only send active url to client
  114. infofile = File.join('data',@r.last,'info.json')
  115. infojson = JSON::parse(File.read(infofile))
  116. hash["url"] = urls.find{ |h| h['lang'] == infojson["lang"]}['url']
  117. @a[0].send(JSON.dump(hash))
  118. nil
  119. end
  120. def headers
  121. Riddl::Header.new('CPEE-CALLBACK', 'true')
  122. end
  123. end
  124. class DeleteFrame < Riddl::Implementation
  125. def response
  126. path = File.join('data',@r.last,'frames.json')
  127. file = File.read(path)
  128. data_hash = JSON::parse(file)
  129. data_hash["data"].each do | c |
  130. if doOverlap(c['lx'], c['ly'], c['rx'], c['ry'], @p[0].value.to_i, @p[1].value.to_i, (@p[0].value.to_i + 1), (@p[1].value.to_i + 1))
  131. data_hash["data"].delete(c)
  132. end
  133. end
  134. File.write(path, JSON.dump(data_hash))
  135. end
  136. end
  137. def doOverlap(l1x, l1y, r1x, r1y, l2x, l2y, r2x, r2y)
  138. if l1x > r2x || l2x > r1x
  139. return false;
  140. end
  141. if l1y > r2y || l2y > r1y
  142. return false;
  143. end
  144. return true;
  145. end
  146. class Delete < Riddl::Implementation
  147. def response
  148. if cbu = File.read(File.join('data',@r.last,'callback'))
  149. send = { 'operation' => @p[0].value }
  150. case send['operation']
  151. when 'result'
  152. send['target'] = JSON::parse(@p[1].value.read)
  153. end
  154. cbu += '/' unless cbu[-1] == '/'
  155. Typhoeus.put(cbu, body: JSON::generate(send), headers: { 'content-type' => 'application/json'})
  156. end
  157. File.unlink(File.join('data',@r.last,'callback')) rescue nil
  158. File.unlink(File.join('data',@r.last,'cpeeinstance.url')) rescue nil
  159. File.unlink(File.join('data',@r.last,'style.url')) rescue nil
  160. File.unlink(File.join('data',@r.last,'document.xml')) rescue nil
  161. File.unlink(File.join('data',@r.last,'info.json')) rescue nil
  162. @a[0].send('reset')
  163. nil
  164. end
  165. end
  166. class GetFrames < Riddl::Implementation #{{{
  167. def response
  168. fname = File.join('data',@r[-2],'frames.json')
  169. if File.exists? fname
  170. infofile = File.join('data',@r[-2],'info.json')
  171. infojson = JSON::parse(File.read(infofile))
  172. #remove not used languages
  173. file = JSON::parse(File.read(fname))
  174. file["data"].each do |child|
  175. child["url"] = child["url"].find{ |h| h['lang'] == infojson["lang"]}['url']
  176. end
  177. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(file))
  178. else
  179. @status = 404
  180. end
  181. end
  182. end #}}}
  183. class SetDataElements < Riddl::Implementation #{{{
  184. def response
  185. savejson = @p.map { |o| Hash[o.name, o.value] }.to_json
  186. path = File.join('data',@r[0],'dataelements.json')
  187. File.write(path, savejson)
  188. #puts xyz
  189. #puts JSON.pretty_generate(@p.to_json)
  190. #puts @p.length()
  191. #puts @p[0].name
  192. #puts @p[0].value
  193. #fname = File.join('data',@r[-2],'dataelements.json')
  194. #if File.exists? fname
  195. # Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
  196. #else
  197. # @status = 404
  198. #end
  199. end
  200. end #}}}
  201. class GetDataElements < Riddl::Implementation #{{{
  202. def response
  203. fname = File.join('data',@r[-2],'dataelements.json')
  204. if File.exists? fname
  205. Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
  206. else
  207. @status = 404
  208. end
  209. end
  210. end #}}}
  211. class GetInfo < Riddl::Implementation #{{{
  212. def response
  213. fname = File.join('data',@r[-2],'info.json')
  214. if File.exists? fname
  215. Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
  216. else
  217. @status = 404
  218. end
  219. end
  220. end #}}}
  221. class GetLangs < Riddl::Implementation #{{{
  222. def response
  223. fname = File.join('data',@r[-2],'info.json')
  224. if File.exists? fname
  225. infojson = JSON::parse(File.read(fname))
  226. Riddl::Parameter::Complex.new('value','application/json',infojson["langs"])
  227. else
  228. @status = 404
  229. end
  230. end
  231. end #}}}
  232. class SetLang < Riddl::Implementation #{{{
  233. def response
  234. fname = File.join('data',@r[-2],'info.json')
  235. if File.exists? fname
  236. infojson = JSON::parse(File.read(fname))
  237. infojson["lang"] = @p[0].value
  238. #add to langs
  239. if !infojson["langs"].include?(@p[0].value)
  240. infojson["langs"].push(@p[0].value)
  241. end
  242. File.write(fname, JSON.dump(infojson))
  243. @a[0].send('reset')
  244. nil
  245. else
  246. @status = 404
  247. end
  248. end
  249. end #}}}
  250. class GetStyle < Riddl::Implementation #{{{
  251. def response
  252. fname = File.join('data',@r[-2],'style.url')
  253. if File.exists? fname
  254. Riddl::Parameter::Complex.new('url','text/plain',File.read(fname).strip)
  255. else
  256. @status = 404
  257. end
  258. end
  259. end #}}}
  260. class GetCpeeInstance < Riddl::Implementation #{{{
  261. def response
  262. fname = File.join('data',@r[-2],'cpeeinstance.url')
  263. if File.exists? fname
  264. Riddl::Parameter::Complex.new('url','text/plain',File.read(fname).strip)
  265. else
  266. @status = 404
  267. end
  268. end
  269. end #}}}
  270. class OutputTest < Riddl::Implementation #{{{
  271. def response
  272. puts "Test"
  273. end
  274. end #}}}
  275. class Handler < Riddl::Implementation
  276. def response
  277. puts "handler"
  278. topic = @p[1].value
  279. event_name = @p[2].value
  280. notification = JSON.parse(@p[3].value.read)
  281. instancenr = notification['instance']
  282. content = notification['content']
  283. activity = content['activity']
  284. parameters = content['parameters']
  285. receiving = content['received']
  286. #puts instancenr
  287. #puts activity
  288. puts content['values']
  289. if content['values']&.any?
  290. #puts alldata['ausfuehrungen']
  291. puts "writing file"
  292. path = File.join('data',@r[0],'dataelements.json')
  293. File.write(path, JSON.dump(content['values']))
  294. end
  295. @a[0].send(@r[0])
  296. nil
  297. nil
  298. end
  299. end
  300. class SSE < Riddl::SSEImplementation #{{{
  301. def onopen
  302. signals = @a[0]
  303. signals.add self
  304. send 'started'
  305. true
  306. end
  307. def onclose
  308. signals = @a[0]
  309. signals.remove self
  310. nil
  311. end
  312. end #}}}
  313. class SSE2 < Riddl::SSEImplementation #{{{
  314. def onopen
  315. signals = @a[0]
  316. signals.add self
  317. send 'started'
  318. true
  319. end
  320. def onclose
  321. signals = @a[0]
  322. signals.remove self
  323. nil
  324. end
  325. end #}}}
  326. class Signaling # {{{
  327. def initialize
  328. @binding = []
  329. end
  330. def add(binding)
  331. @binding << binding
  332. end
  333. def remove(binding)
  334. @binding.delete(binding)
  335. end
  336. def length
  337. @binding.length
  338. end
  339. def send(value)
  340. @binding.each do |b|
  341. b.send(value)
  342. end
  343. end
  344. end #}}}
  345. server = Riddl::Server.new(File.join(__dir__,'/frames.xml'), :host => 'localhost') do |opts|
  346. accessible_description true
  347. cross_site_xhr true
  348. opts[:signals] = {}
  349. opts[:signals2] = {}
  350. parallel do
  351. loop do
  352. opts[:signals].each do |k,v|
  353. v.send('keepalive')
  354. end
  355. opts[:signals2].each do |k,v|
  356. v.send('keepalive')
  357. end
  358. sleep 5
  359. end
  360. end
  361. on resource do
  362. on resource do |r|
  363. idx = r[:r][0]
  364. opts[:signals][idx] ||= Signaling.new
  365. opts[:signals2]["handler"] ||= Signaling.new
  366. run Get, "test" if get
  367. run InitFrame, opts[:signals][idx] if post 'input'
  368. run NewFrameSet, opts[:signals][idx] if put 'sframe'
  369. run NewFrameWait, opts[:signals][idx] if put 'wframe'
  370. run DeleteFrame, opts[:signals][idx] if post 'deleteframe'
  371. on resource 'handler' do
  372. run Handler, opts[:signals2]["handler"] if post
  373. on resource 'sse' do
  374. run SSE2, opts[:signals2]["handler"] if sse
  375. end
  376. end
  377. run Delete, opts[:signals][idx] if delete 'opa'
  378. run Delete, opts[:signals][idx] if delete 'opb'
  379. on resource 'sse' do
  380. run SSE, opts[:signals][idx] if sse
  381. end
  382. on resource 'languages' do
  383. run GetLangs if get
  384. run SetLang, opts[:signals][idx] if post 'lang'
  385. end
  386. on resource 'style.url' do
  387. run GetStyle if get
  388. end
  389. on resource 'cpeeinstance.url' do
  390. run GetCpeeInstance if get
  391. end
  392. on resource 'info.json' do
  393. run GetInfo if get
  394. end
  395. on resource 'frames.json' do
  396. run GetFrames if get
  397. end
  398. on resource 'test' do
  399. run OutputTest if put
  400. end
  401. on resource 'dataelements.json' do
  402. run SetDataElements if post
  403. run GetDataElements if get
  404. end
  405. end
  406. end
  407. end.loop!