123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 |
- #!/usr/bin/ruby
- #
- # This file is part of CPEE-FRAMES.
- #
- # CPEE-FRAMES is free software: you can redistribute it and/or
- # modify it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or (at your
- # option) any later version.
- #
- # CPEE-FRAMES is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
- # Public License for more details.
- #
- # You should have received a copy of the GNU General Public License along with
- # CPEE-FRAMES (file LICENSE in the main directory). If not, see
- # <http://www.gnu.org/licenses/>.
- require 'rubygems'
- require 'json'
- require 'xml/smart'
- require 'riddl/server'
- require 'fileutils'
- require 'typhoeus'
- module CPEE
- module Frames
- SERVER = File.expand_path(File.join(__dir__,'frames.xml'))
- # https://coderwall.com/p/atyfyq/ruby-string-to-boolean
- # showbutton
- refine String do #{{{
- def to_bool
- return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
- return false if self == false || self.empty? || self =~ (/(false|f|no|n|0)$/i)
- raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
- end
- end #}}}
- class GetTutorial < Riddl::Implementation #{{{
- def response
- Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'ui','tutorial.html')))
- end
- end #}}}
- class GetAllConfigs < Riddl::Implementation #{{{
- def response
- data_dir = @a[0]
- databack = JSON::pretty_generate(Dir.glob(File.join(data_dir,'*')).map{|f| File.basename(f)}.sort_by{|x| x.downcase})
- Riddl::Parameter::Complex.new('list','application/json',databack)
- end
- end #}}}
- class Get < Riddl::Implementation #{{{
- def response
- if @r[0] == 'test'
- Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'ui','test.html')))
- elsif @r[0] == 'menu'
- Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'ui','menu.html')))
- elsif @r[0] == 'framedata'
- Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'ui','framedata.html')))
- else
- Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'ui','template.html')))
- end
- end
- end #}}}
- class InitFrame < Riddl::Implementation #{{{
- def response
- Dir.mkdir(File.join('data',@r.last)) rescue nil
- if !@p[0].value.to_s.empty?
- File.write(File.join('data',@r.last,'style.url'),@p[0].value)
- end
- File.write(File.join('data',@r.last,'frames.json'),JSON.dump(JSON.parse('{"data":[]}')))
- #for handler
- File.write(File.join('data',@r.last,'dataelements.json'),JSON.dump(JSON.parse('{"data":[]}')))
- 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 + '"}')))
- File.write(File.join('data',@r.last,'callback'),@h['CPEE_CALLBACK'])
- File.write(File.join('data',@r.last,'cpeeinstance.url'),@h['CPEE_INSTANCE_URL'])
- @a[0].send('new')
- nil
- end
- end #}}}
- class NewFrameSet < Riddl::Implementation
- def response
- path = File.join('data',@r.last,'frames.json')
- file = File.read(path)
- data_hash = JSON::parse(file)
- #check if new frame overlaps others if it does, delete overlapped frames
- data_hash["data"].each do | c |
- if CPEE::Frames::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))
- data_hash["data"].delete(c)
- end
- end
- #check if url is set
- if @p[7].value != ""
- urls = JSON::parse(@p[7].value);
- if @p[8].value == ""
- 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']};
- else
- 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']};
- end
- data_hash["data"].push(hash)
- File.write(path, JSON.dump(data_hash))
- #only send active url to client
- infofile = File.join('data',@r.last,'info.json')
- infojson = JSON::parse(File.read(infofile))
- hash["url"] = urls.find{ |h| h['lang'] == infojson["lang"]}['url']
- @a[0].send(JSON.dump(hash))
- else
- File.write(path, JSON.dump(data_hash))
- 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']};
- @a[0].send(JSON.dump(hash))
- end
- nil
- end
- end
- class NewFrameWait < Riddl::Implementation
- def response
- path = File.join('data',@r.last,'frames.json')
- file = File.read(path)
- data_hash = JSON::parse(file)
- #check if new frame overlaps others if it does, delete overlapped frames
- data_hash["data"].each do | c |
- if CPEE::Frames::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))
- data_hash["data"].delete(c)
- end
- end
- #check if url is set
- if @p[7].value != ""
- urls = JSON::parse(@p[7].value);
- if @p[8].value == ""
- 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']};
- else
- 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']};
- end
- data_hash["data"].push(hash)
- File.write(path, JSON.dump(data_hash))
- #only send active url to client
- infofile = File.join('data',@r.last,'info.json')
- infojson = JSON::parse(File.read(infofile))
- hash["url"] = urls.find{ |h| h['lang'] == infojson["lang"]}['url']
- File.write(File.join('data',@r.last,'callback'),@h['CPEE_CALLBACK'])
- @a[0].send(JSON.dump(hash))
- else
- File.write(path, JSON.dump(data_hash))
- 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']};
- File.write(File.join('data',@r.last,'callback'),@h['CPEE_CALLBACK'])
- @a[0].send(JSON.dump(hash))
- Typhoeus.put(@h['CPEE_CALLBACK'], body: "No Frame Set")
- end
- nil
- end
- def headers
- Riddl::Header.new('CPEE-CALLBACK', 'true')
- end
- end
- class DeleteFrame < Riddl::Implementation
- def response
- path = File.join('data',@r.last,'frames.json')
- file = File.read(path)
- data_hash = JSON::parse(file)
- data_hash["data"].each do | c |
- if CPEE::Frames::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))
- data_hash["data"].delete(c)
- end
- end
- File.write(path, JSON.dump(data_hash))
- end
- end
- def self::doOverlap(l1x, l1y, r1x, r1y, l2x, l2y, r2x, r2y)
- if l1x > r2x || l2x > r1x
- return false
- end
- if l1y > r2y || l2y > r1y
- return false
- end
- return true
- end
- class Delete < Riddl::Implementation
- def response
- pp "in delete"
- if cbu = File.read(File.join('data',@r.last,'callback'))
- pp "XYZ"
- send = { 'operation' => @p[0].value }
- case send['operation']
- when 'result'
- send['target'] = JSON::parse(@p[1].value.read)
- end
- cbu += '/' unless cbu[-1] == '/'
- pp "Sending"
- Typhoeus.put(cbu, body: JSON::generate(send), headers: { 'content-type' => 'application/json'})
- end
- #File.unlink(File.join('data',@r.last,'callback')) rescue nil
- #File.unlink(File.join('data',@r.last,'cpeeinstance.url')) rescue nil
- #File.unlink(File.join('data',@r.last,'style.url')) rescue nil
- #File.unlink(File.join('data',@r.last,'document.xml')) rescue nil
- #File.unlink(File.join('data',@r.last,'info.json')) rescue nil
- @a[0].send('reset')
- nil
- end
- end
- class GetFrames < Riddl::Implementation #{{{
- def response
- fname = File.join('data',@r[-2],'frames.json')
- if File.exist? fname
- infofile = File.join('data',@r[-2],'info.json')
- infojson = JSON::parse(File.read(infofile))
- #remove not used languages
- file = JSON::parse(File.read(fname))
- file["data"].each do |child|
- child["url"] = child["url"].find{ |h| h['lang'] == infojson["lang"]}['url']
- end
- Riddl::Parameter::Complex.new('value','application/json',JSON.dump(file))
- else
- @status = 404
- end
- end
- end #}}}
- class SetDataElements < Riddl::Implementation #{{{
- def response
- savejson = @p.map { |o| Hash[o.name, o.value] }.to_json
- path = File.join('data',@r[0],'dataelements.json')
- File.write(path, savejson)
- #puts xyz
- #puts JSON.pretty_generate(@p.to_json)
- #puts @p.length()
- #puts @p[0].name
- #puts @p[0].value
- #fname = File.join('data',@r[-2],'dataelements.json')
- #if File.exist? fname
- # Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
- #else
- # @status = 404
- #end
- end
- end #}}}
- class GetDataElements < Riddl::Implementation #{{{
- def response
- fname = File.join('data',@r[-2],'dataelements.json')
- if File.exist? fname
- Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
- else
- @status = 404
- end
- end
- end #}}}
- class GetInfo < Riddl::Implementation #{{{
- def response
- fname = File.join('data',@r[-2],'info.json')
- if File.exist? fname
- Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
- else
- @status = 404
- end
- end
- end #}}}
- class GetLangs < Riddl::Implementation #{{{
- def response
- fname = File.join('data',@r[-2],'info.json')
- if File.exist? fname
- infojson = JSON::parse(File.read(fname))
- Riddl::Parameter::Complex.new('value','application/json',infojson["langs"])
- else
- @status = 404
- end
- end
- end #}}}
- class SetLang < Riddl::Implementation #{{{
- def response
- fname = File.join('data',@r[-2],'info.json')
- if File.exist? fname
- infojson = JSON::parse(File.read(fname))
- infojson["lang"] = @p[0].value
- #add to langs
- if !infojson["langs"].include?(@p[0].value)
- infojson["langs"].push(@p[0].value)
- end
- File.write(fname, JSON.dump(infojson))
- @a[0].send('reset')
- nil
- else
- @status = 404
- end
- end
- end #}}}
- class GetStyle < Riddl::Implementation #{{{
- def response
- fname = File.join('data',@r[-2],'style.url')
- if File.exist? fname
- Riddl::Parameter::Complex.new('url','text/plain',File.read(fname).strip)
- else
- @status = 404
- end
- end
- end #}}}
- class GetCpeeInstance < Riddl::Implementation #{{{
- def response
- fname = File.join('data',@r[-2],'cpeeinstance.url')
- if File.exist? fname
- Riddl::Parameter::Complex.new('url','text/plain',File.read(fname).strip)
- else
- @status = 404
- end
- end
- end #}}}
- class OutputTest < Riddl::Implementation #{{{
- def response
- puts "Test"
- end
- end #}}}
- class Handler < Riddl::Implementation
- def response
- topic = @p[1].value
- event_name = @p[2].value
- notification = JSON.parse(@p[3].value.read)
- instancenr = notification['instance']
- content = notification['content']
- activity = content['activity']
- parameters = content['parameters']
- receiving = content['received']
- #puts instancenr
- #puts activity
- puts content['values']
- if content['values']&.any?
- #puts alldata['ausfuehrungen']
- puts "writing file"
- path = File.join('data',@r[0],'dataelements.json')
- File.write(path, JSON.dump(content['values']))
- end
- @a[0].send(@r[0])
- nil
- end
- end
- class SSE < Riddl::SSEImplementation #{{{
- def onopen
- signals = @a[0]
- signals.add self
- send 'started'
- true
- end
- def onclose
- signals = @a[0]
- signals.remove self
- nil
- end
- end #}}}
- class SSE2 < Riddl::SSEImplementation #{{{
- def onopen
- signals = @a[0]
- signals.add self
- send 'started'
- true
- end
- def onclose
- signals = @a[0]
- signals.remove self
- nil
- end
- end #}}}
- class Signaling # {{{
- def initialize
- @binding = []
- end
- def add(binding)
- @binding << binding
- end
- def remove(binding)
- @binding.delete(binding)
- end
- def length
- @binding.length
- end
- def send(value)
- @binding.each do |b|
- b.send(value)
- end
- end
- end #}}}
- def self::implementation(opts)
- opts[:signals] = {}
- opts[:signals2] = {}
- opts[:data_dir] ||= File.expand_path(File.join(__dir__,'data'))
- Proc.new do
- parallel do
- loop do
- opts[:signals].each do |k,v|
- v.send('keepalive')
- end
- opts[:signals2].each do |k,v|
- v.send('keepalive')
- end
- sleep 5
- end
- end
- on resource do
- run GetTutorial if get
- on resource 'getConfigs' do
- run GetAllConfigs, opts[:data_dir] if get
- end
- on resource do |r|
- idx = r[:r][0]
- opts[:signals][idx] ||= Signaling.new
- opts[:signals2]["handler"] ||= Signaling.new
- run Get, "test" if get
- run InitFrame, opts[:signals][idx] if post 'input'
- run NewFrameSet, opts[:signals][idx] if put 'sframe'
- run NewFrameWait, opts[:signals][idx] if put 'wframe'
- run DeleteFrame, opts[:signals][idx] if post 'deleteframe'
- on resource 'handler' do
- run Handler, opts[:signals2]["handler"] if post
- on resource 'sse' do
- run SSE2, opts[:signals2]["handler"] if sse
- end
- end
- run Delete, opts[:signals][idx] if delete 'opa'
- run Delete, opts[:signals][idx] if delete 'opb'
- run Delete, opts[:signals][idx] if delete 'opc'
- on resource 'sse' do
- run SSE, opts[:signals][idx] if sse
- end
- on resource 'languages' do
- run GetLangs if get
- run SetLang, opts[:signals][idx] if post 'lang'
- end
- on resource 'style.url' do
- run GetStyle if get
- end
- on resource 'cpeeinstance.url' do
- run GetCpeeInstance if get
- end
- on resource 'info.json' do
- run GetInfo if get
- end
- on resource 'frames.json' do
- run GetFrames if get
- end
- on resource 'test' do
- run OutputTest if put
- end
- on resource 'dataelements.json' do
- run SetDataElements if post
- run GetDataElements if get
- end
- end
- end
- end
- end
- end
- end
|