frames 14 KB

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