frames 15 KB

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