frames 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  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.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 + '"], "document_name": "' + @p[5].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. #check if url is set
  79. if @p[7].value != ""
  80. urls = JSON::parse(@p[7].value);
  81. if @p[8].value == ""
  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, style: @p[6].value, default: "{}", callback: @h['CPEE_CALLBACK']};
  83. else
  84. 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, style: @p[6].value, default: JSON::parse(@p[8].value), callback: @h['CPEE_CALLBACK']};
  85. end
  86. data_hash["data"].push(hash)
  87. File.write(path, JSON.dump(data_hash))
  88. #only send active url to client
  89. infofile = File.join('data',@r.last,'info.json')
  90. infojson = JSON::parse(File.read(infofile))
  91. hash["url"] = urls.find{ |h| h['lang'] == infojson["lang"]}['url']
  92. @a[0].send(JSON.dump(hash))
  93. else
  94. File.write(path, JSON.dump(data_hash))
  95. 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: "empty", showbutton: @p[5].value, style: @p[6].value, default: "{}", callback: @h['CPEE_CALLBACK']};
  96. @a[0].send(JSON.dump(hash))
  97. end
  98. nil
  99. end
  100. end
  101. class NewFrameWait < Riddl::Implementation
  102. def response
  103. path = File.join('data',@r.last,'frames.json')
  104. file = File.read(path)
  105. data_hash = JSON::parse(file)
  106. #check if new frame overlaps others if it does, delete overlapped frames
  107. data_hash["data"].each do | c |
  108. 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))
  109. data_hash["data"].delete(c)
  110. end
  111. end
  112. urls = JSON::parse(@p[7].value);
  113. if @p[8].value == ""
  114. 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, style: @p[6].value, default: "{}", callback: @h['CPEE_CALLBACK']};
  115. else
  116. 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, style: @p[6].value, default: JSON::parse(@p[8].value), callback: @h['CPEE_CALLBACK']};
  117. end
  118. data_hash["data"].push(hash)
  119. File.write(path, JSON.dump(data_hash))
  120. #only send active url to client
  121. infofile = File.join('data',@r.last,'info.json')
  122. infojson = JSON::parse(File.read(infofile))
  123. hash["url"] = urls.find{ |h| h['lang'] == infojson["lang"]}['url']
  124. @a[0].send(JSON.dump(hash))
  125. nil
  126. end
  127. def headers
  128. Riddl::Header.new('CPEE-CALLBACK', 'true')
  129. end
  130. end
  131. class DeleteFrame < Riddl::Implementation
  132. def response
  133. path = File.join('data',@r.last,'frames.json')
  134. file = File.read(path)
  135. data_hash = JSON::parse(file)
  136. data_hash["data"].each do | c |
  137. 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))
  138. data_hash["data"].delete(c)
  139. end
  140. end
  141. File.write(path, JSON.dump(data_hash))
  142. end
  143. end
  144. def doOverlap(l1x, l1y, r1x, r1y, l2x, l2y, r2x, r2y)
  145. if l1x > r2x || l2x > r1x
  146. return false;
  147. end
  148. if l1y > r2y || l2y > r1y
  149. return false;
  150. end
  151. return true;
  152. end
  153. class Delete < Riddl::Implementation
  154. def response
  155. if cbu = File.read(File.join('data',@r.last,'callback'))
  156. send = { 'operation' => @p[0].value }
  157. case send['operation']
  158. when 'result'
  159. send['target'] = JSON::parse(@p[1].value.read)
  160. end
  161. cbu += '/' unless cbu[-1] == '/'
  162. Typhoeus.put(cbu, body: JSON::generate(send), headers: { 'content-type' => 'application/json'})
  163. end
  164. File.unlink(File.join('data',@r.last,'callback')) rescue nil
  165. File.unlink(File.join('data',@r.last,'cpeeinstance.url')) rescue nil
  166. File.unlink(File.join('data',@r.last,'style.url')) rescue nil
  167. File.unlink(File.join('data',@r.last,'document.xml')) rescue nil
  168. File.unlink(File.join('data',@r.last,'info.json')) rescue nil
  169. @a[0].send('reset')
  170. nil
  171. end
  172. end
  173. class GetFrames < Riddl::Implementation #{{{
  174. def response
  175. fname = File.join('data',@r[-2],'frames.json')
  176. if File.exists? fname
  177. infofile = File.join('data',@r[-2],'info.json')
  178. infojson = JSON::parse(File.read(infofile))
  179. #remove not used languages
  180. file = JSON::parse(File.read(fname))
  181. file["data"].each do |child|
  182. child["url"] = child["url"].find{ |h| h['lang'] == infojson["lang"]}['url']
  183. end
  184. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(file))
  185. else
  186. @status = 404
  187. end
  188. end
  189. end #}}}
  190. class SetDataElements < Riddl::Implementation #{{{
  191. def response
  192. savejson = @p.map { |o| Hash[o.name, o.value] }.to_json
  193. path = File.join('data',@r[0],'dataelements.json')
  194. File.write(path, savejson)
  195. #puts xyz
  196. #puts JSON.pretty_generate(@p.to_json)
  197. #puts @p.length()
  198. #puts @p[0].name
  199. #puts @p[0].value
  200. #fname = File.join('data',@r[-2],'dataelements.json')
  201. #if File.exists? fname
  202. # Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
  203. #else
  204. # @status = 404
  205. #end
  206. end
  207. end #}}}
  208. class GetDataElements < Riddl::Implementation #{{{
  209. def response
  210. fname = File.join('data',@r[-2],'dataelements.json')
  211. if File.exists? fname
  212. Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
  213. else
  214. @status = 404
  215. end
  216. end
  217. end #}}}
  218. class GetInfo < Riddl::Implementation #{{{
  219. def response
  220. fname = File.join('data',@r[-2],'info.json')
  221. if File.exists? fname
  222. Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
  223. else
  224. @status = 404
  225. end
  226. end
  227. end #}}}
  228. class GetLangs < Riddl::Implementation #{{{
  229. def response
  230. fname = File.join('data',@r[-2],'info.json')
  231. if File.exists? fname
  232. infojson = JSON::parse(File.read(fname))
  233. Riddl::Parameter::Complex.new('value','application/json',infojson["langs"])
  234. else
  235. @status = 404
  236. end
  237. end
  238. end #}}}
  239. class SetLang < Riddl::Implementation #{{{
  240. def response
  241. fname = File.join('data',@r[-2],'info.json')
  242. if File.exists? fname
  243. infojson = JSON::parse(File.read(fname))
  244. infojson["lang"] = @p[0].value
  245. #add to langs
  246. if !infojson["langs"].include?(@p[0].value)
  247. infojson["langs"].push(@p[0].value)
  248. end
  249. File.write(fname, JSON.dump(infojson))
  250. @a[0].send('reset')
  251. nil
  252. else
  253. @status = 404
  254. end
  255. end
  256. end #}}}
  257. class GetStyle < Riddl::Implementation #{{{
  258. def response
  259. fname = File.join('data',@r[-2],'style.url')
  260. if File.exists? fname
  261. Riddl::Parameter::Complex.new('url','text/plain',File.read(fname).strip)
  262. else
  263. @status = 404
  264. end
  265. end
  266. end #}}}
  267. class GetCpeeInstance < Riddl::Implementation #{{{
  268. def response
  269. fname = File.join('data',@r[-2],'cpeeinstance.url')
  270. if File.exists? fname
  271. Riddl::Parameter::Complex.new('url','text/plain',File.read(fname).strip)
  272. else
  273. @status = 404
  274. end
  275. end
  276. end #}}}
  277. class OutputTest < Riddl::Implementation #{{{
  278. def response
  279. puts "Test"
  280. end
  281. end #}}}
  282. class Handler < Riddl::Implementation
  283. def response
  284. puts "handler"
  285. topic = @p[1].value
  286. event_name = @p[2].value
  287. notification = JSON.parse(@p[3].value.read)
  288. instancenr = notification['instance']
  289. content = notification['content']
  290. activity = content['activity']
  291. parameters = content['parameters']
  292. receiving = content['received']
  293. #puts instancenr
  294. #puts activity
  295. puts content['values']
  296. if content['values']&.any?
  297. #puts alldata['ausfuehrungen']
  298. puts "writing file"
  299. path = File.join('data',@r[0],'dataelements.json')
  300. File.write(path, JSON.dump(content['values']))
  301. end
  302. @a[0].send(@r[0])
  303. nil
  304. nil
  305. end
  306. end
  307. class SSE < Riddl::SSEImplementation #{{{
  308. def onopen
  309. signals = @a[0]
  310. signals.add self
  311. send 'started'
  312. true
  313. end
  314. def onclose
  315. signals = @a[0]
  316. signals.remove self
  317. nil
  318. end
  319. end #}}}
  320. class SSE2 < Riddl::SSEImplementation #{{{
  321. def onopen
  322. signals = @a[0]
  323. signals.add self
  324. send 'started'
  325. true
  326. end
  327. def onclose
  328. signals = @a[0]
  329. signals.remove self
  330. nil
  331. end
  332. end #}}}
  333. class Signaling # {{{
  334. def initialize
  335. @binding = []
  336. end
  337. def add(binding)
  338. @binding << binding
  339. end
  340. def remove(binding)
  341. @binding.delete(binding)
  342. end
  343. def length
  344. @binding.length
  345. end
  346. def send(value)
  347. @binding.each do |b|
  348. b.send(value)
  349. end
  350. end
  351. end #}}}
  352. server = Riddl::Server.new(File.join(__dir__,'/frames.xml'), :host => 'localhost') do |opts|
  353. accessible_description true
  354. cross_site_xhr true
  355. opts[:signals] = {}
  356. opts[:signals2] = {}
  357. parallel do
  358. loop do
  359. opts[:signals].each do |k,v|
  360. v.send('keepalive')
  361. end
  362. opts[:signals2].each do |k,v|
  363. v.send('keepalive')
  364. end
  365. sleep 5
  366. end
  367. end
  368. on resource do
  369. on resource do |r|
  370. idx = r[:r][0]
  371. opts[:signals][idx] ||= Signaling.new
  372. opts[:signals2]["handler"] ||= Signaling.new
  373. run Get, "test" if get
  374. run InitFrame, opts[:signals][idx] if post 'input'
  375. run NewFrameSet, opts[:signals][idx] if put 'sframe'
  376. run NewFrameWait, opts[:signals][idx] if put 'wframe'
  377. run DeleteFrame, opts[:signals][idx] if post 'deleteframe'
  378. on resource 'handler' do
  379. run Handler, opts[:signals2]["handler"] if post
  380. on resource 'sse' do
  381. run SSE2, opts[:signals2]["handler"] if sse
  382. end
  383. end
  384. run Delete, opts[:signals][idx] if delete 'opa'
  385. run Delete, opts[:signals][idx] if delete 'opb'
  386. on resource 'sse' do
  387. run SSE, opts[:signals][idx] if sse
  388. end
  389. on resource 'languages' do
  390. run GetLangs if get
  391. run SetLang, opts[:signals][idx] if post 'lang'
  392. end
  393. on resource 'style.url' do
  394. run GetStyle if get
  395. end
  396. on resource 'cpeeinstance.url' do
  397. run GetCpeeInstance if get
  398. end
  399. on resource 'info.json' do
  400. run GetInfo if get
  401. end
  402. on resource 'frames.json' do
  403. run GetFrames if get
  404. end
  405. on resource 'test' do
  406. run OutputTest if put
  407. end
  408. on resource 'dataelements.json' do
  409. run SetDataElements if post
  410. run GetDataElements if get
  411. end
  412. end
  413. end
  414. end.loop!