frames 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  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 GetDataElements < Riddl::Implementation #{{{
  178. def response
  179. fname = File.join('data',@r[-2],'dataelements.json')
  180. if File.exists? fname
  181. Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
  182. else
  183. @status = 404
  184. end
  185. end
  186. end #}}}
  187. class GetInfo < Riddl::Implementation #{{{
  188. def response
  189. fname = File.join('data',@r[-2],'info.json')
  190. if File.exists? fname
  191. Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
  192. else
  193. @status = 404
  194. end
  195. end
  196. end #}}}
  197. class GetLangs < Riddl::Implementation #{{{
  198. def response
  199. fname = File.join('data',@r[-2],'info.json')
  200. if File.exists? fname
  201. infojson = JSON::parse(File.read(fname))
  202. Riddl::Parameter::Complex.new('value','application/json',infojson["langs"])
  203. else
  204. @status = 404
  205. end
  206. end
  207. end #}}}
  208. class SetLang < Riddl::Implementation #{{{
  209. def response
  210. fname = File.join('data',@r[-2],'info.json')
  211. if File.exists? fname
  212. infojson = JSON::parse(File.read(fname))
  213. infojson["lang"] = @p[0].value
  214. #add to langs
  215. if !infojson["langs"].include?(@p[0].value)
  216. infojson["langs"].push(@p[0].value)
  217. end
  218. File.write(fname, JSON.dump(infojson))
  219. @a[0].send('reset')
  220. nil
  221. else
  222. @status = 404
  223. end
  224. end
  225. end #}}}
  226. class GetStyle < Riddl::Implementation #{{{
  227. def response
  228. fname = File.join('data',@r[-2],'style.url')
  229. if File.exists? fname
  230. Riddl::Parameter::Complex.new('url','text/plain',File.read(fname).strip)
  231. else
  232. @status = 404
  233. end
  234. end
  235. end #}}}
  236. class GetCpeeInstance < Riddl::Implementation #{{{
  237. def response
  238. fname = File.join('data',@r[-2],'cpeeinstance.url')
  239. if File.exists? fname
  240. Riddl::Parameter::Complex.new('url','text/plain',File.read(fname).strip)
  241. else
  242. @status = 404
  243. end
  244. end
  245. end #}}}
  246. class OutputTest < Riddl::Implementation #{{{
  247. def response
  248. puts "Test"
  249. end
  250. end #}}}
  251. class Handler < Riddl::Implementation
  252. def response
  253. puts "handler"
  254. topic = @p[1].value
  255. event_name = @p[2].value
  256. notification = JSON.parse(@p[3].value.read)
  257. instancenr = notification['instance']
  258. content = notification['content']
  259. activity = content['activity']
  260. parameters = content['parameters']
  261. receiving = content['received']
  262. #puts instancenr
  263. #puts activity
  264. puts content['values']
  265. if content['values']&.any?
  266. #puts alldata['ausfuehrungen']
  267. puts "writing file"
  268. path = File.join('data',@r[0],'dataelements.json')
  269. File.write(path, JSON.dump(content['values']))
  270. end
  271. @a[0].send(@r[0])
  272. nil
  273. nil
  274. end
  275. end
  276. class SSE < Riddl::SSEImplementation #{{{
  277. def onopen
  278. signals = @a[0]
  279. signals.add self
  280. send 'started'
  281. true
  282. end
  283. def onclose
  284. signals = @a[0]
  285. signals.remove self
  286. nil
  287. end
  288. end #}}}
  289. class SSE2 < Riddl::SSEImplementation #{{{
  290. def onopen
  291. signals = @a[0]
  292. signals.add self
  293. send 'started'
  294. true
  295. end
  296. def onclose
  297. signals = @a[0]
  298. signals.remove self
  299. nil
  300. end
  301. end #}}}
  302. class Signaling # {{{
  303. def initialize
  304. @binding = []
  305. end
  306. def add(binding)
  307. @binding << binding
  308. end
  309. def remove(binding)
  310. @binding.delete(binding)
  311. end
  312. def length
  313. @binding.length
  314. end
  315. def send(value)
  316. @binding.each do |b|
  317. b.send(value)
  318. end
  319. end
  320. end #}}}
  321. server = Riddl::Server.new(File.join(__dir__,'/frames.xml'), :host => 'localhost') do |opts|
  322. accessible_description true
  323. cross_site_xhr true
  324. opts[:signals] = {}
  325. opts[:signals2] = {}
  326. parallel do
  327. loop do
  328. opts[:signals].each do |k,v|
  329. v.send('keepalive')
  330. end
  331. opts[:signals2].each do |k,v|
  332. v.send('keepalive')
  333. end
  334. sleep 5
  335. end
  336. end
  337. on resource do
  338. on resource do |r|
  339. idx = r[:r][0]
  340. opts[:signals][idx] ||= Signaling.new
  341. opts[:signals2]["handler"] ||= Signaling.new
  342. run Get, "test" if get
  343. run Put, opts[:signals][idx] if put 'input'
  344. run NewFrame, opts[:signals][idx] if post 'frame'
  345. run NewFramePut, opts[:signals][idx] if put 'frame'
  346. run DeleteFrame, opts[:signals][idx] if post 'deleteframe'
  347. on resource 'handler' do
  348. run Handler, opts[:signals2]["handler"] if post
  349. on resource 'sse' do
  350. run SSE2, opts[:signals2]["handler"] if sse
  351. end
  352. end
  353. run Delete, opts[:signals][idx] if delete 'opa'
  354. run Delete, opts[:signals][idx] if delete 'opb'
  355. on resource 'sse' do
  356. run SSE, opts[:signals][idx] if sse
  357. end
  358. on resource 'languages' do
  359. run GetLangs if get
  360. run SetLang, opts[:signals][idx] if post 'lang'
  361. end
  362. on resource 'style.url' do
  363. run GetStyle if get
  364. end
  365. on resource 'cpeeinstance.url' do
  366. run GetCpeeInstance if get
  367. end
  368. on resource 'info.json' do
  369. run GetInfo if get
  370. end
  371. on resource 'frames.json' do
  372. run GetFrames if get
  373. end
  374. on resource 'test' do
  375. run OutputTest if put
  376. end
  377. on resource 'dataelements.json' do
  378. run GetDataElements if get
  379. end
  380. end
  381. end
  382. end.loop!