frames 15 KB

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