frames 13 KB

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