frames 15 KB

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