diff --git a/.gitignore b/.gitignore
index 288c5a2d..214def79 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,4 +37,11 @@
/app/assets/builds/*
!/app/assets/builds/.keep
+
+/coverage
+
/node_modules
+/doc
+/logs
+
+/.idea
diff --git a/.rspec b/.rspec
new file mode 100644
index 00000000..82b8369c
--- /dev/null
+++ b/.rspec
@@ -0,0 +1 @@
+--require spec_helper
\ No newline at end of file
diff --git a/.ruby-version b/.ruby-version
index 849c0c47..a603bb50 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-ruby-2.7.5
+2.7.5
diff --git a/Gemfile b/Gemfile
index b4f7b825..2facfb93 100644
--- a/Gemfile
+++ b/Gemfile
@@ -70,3 +70,15 @@ group :test do
gem "selenium-webdriver"
gem "webdrivers"
end
+
+gem 'rspec', '~> 3.12'
+
+gem "simplecov", "~> 0.22.0", :group => :test, :require => false
+
+gem "simplecov_json_formatter", "~> 0.1.4", :group => :test, :require => false
+
+gem "rspec-rails", "~> 6.1", :groups => [:development, :test]
+
+gem "rubycritic", require: false
+
+gem "rdoc"
\ No newline at end of file
diff --git a/Gemfile.lock b/Gemfile.lock
index f5dbc2bb..aa9658c9 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -76,6 +76,11 @@ GEM
tzinfo (~> 2.0)
addressable (2.8.5)
public_suffix (>= 2.0.2, < 6.0)
+ ast (2.4.2)
+ axiom-types (0.1.1)
+ descendants_tracker (~> 0.0.4)
+ ice_nine (~> 0.11.0)
+ thread_safe (~> 0.3, >= 0.3.1)
base64 (0.2.0)
bigdecimal (3.1.4)
bindex (0.8.1)
@@ -94,6 +99,8 @@ GEM
capybara-screenshot (1.0.26)
capybara (>= 1.0, < 4)
launchy
+ coercible (1.0.0)
+ descendants_tracker (~> 0.0.1)
concurrent-ruby (1.2.2)
connection_pool (2.4.1)
crass (1.0.6)
@@ -139,15 +146,28 @@ GEM
debug (1.8.0)
irb (>= 1.5.0)
reline (>= 0.3.1)
+ descendants_tracker (0.0.4)
+ thread_safe (~> 0.3, >= 0.3.1)
diff-lcs (1.5.0)
+ docile (1.4.0)
drb (2.2.0)
ruby2_keywords
erubi (1.12.0)
ffi (1.16.3)
+ flay (2.13.1)
+ erubi (~> 1.10)
+ path_expander (~> 1.0)
+ ruby_parser (~> 3.0)
+ sexp_processor (~> 4.0)
+ flog (4.8.0)
+ path_expander (~> 1.0)
+ ruby_parser (~> 3.1, > 3.1.0)
+ sexp_processor (~> 4.8)
globalid (1.2.1)
activesupport (>= 6.1)
i18n (1.14.1)
concurrent-ruby (~> 1.0)
+ ice_nine (0.11.2)
io-console (0.6.0)
irb (1.8.3)
rdoc
@@ -157,6 +177,7 @@ GEM
activesupport (>= 5.0.0)
jsbundling-rails (1.2.1)
railties (>= 6.0.0)
+ kwalify (0.7.2)
launchy (2.5.2)
addressable (~> 2.8)
loofah (2.21.4)
@@ -169,6 +190,7 @@ GEM
net-smtp
marcel (1.0.2)
matrix (0.4.2)
+ metric_fu-Saikuro (1.1.3)
mime-types (3.5.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2023.1003)
@@ -193,6 +215,10 @@ GEM
racc (~> 1.4)
nokogiri (1.15.4-x86_64-linux)
racc (~> 1.4)
+ parser (3.2.2.4)
+ ast (~> 2.4.1)
+ racc
+ path_expander (1.1.1)
psych (5.1.1.1)
stringio
public_suffix (5.0.3)
@@ -236,19 +262,66 @@ GEM
rake (>= 12.2)
thor (~> 1.0, >= 1.2.2)
zeitwerk (~> 2.6)
+ rainbow (3.1.1)
rake (13.1.0)
rdoc (6.6.0)
psych (>= 4.0.0)
+ reek (6.1.4)
+ kwalify (~> 0.7.0)
+ parser (~> 3.2.0)
+ rainbow (>= 2.0, < 4.0)
regexp_parser (2.8.2)
reline (0.4.0)
io-console (~> 0.5)
rexml (3.2.6)
+ rspec (3.12.0)
+ rspec-core (~> 3.12.0)
+ rspec-expectations (~> 3.12.0)
+ rspec-mocks (~> 3.12.0)
+ rspec-core (3.12.2)
+ rspec-support (~> 3.12.0)
+ rspec-expectations (3.12.3)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.12.0)
+ rspec-mocks (3.12.6)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.12.0)
+ rspec-rails (6.1.0)
+ actionpack (>= 6.1)
+ activesupport (>= 6.1)
+ railties (>= 6.1)
+ rspec-core (~> 3.12)
+ rspec-expectations (~> 3.12)
+ rspec-mocks (~> 3.12)
+ rspec-support (~> 3.12)
+ rspec-support (3.12.1)
ruby2_keywords (0.0.5)
+ ruby_parser (3.20.3)
+ sexp_processor (~> 4.16)
+ rubycritic (4.9.0)
+ flay (~> 2.13)
+ flog (~> 4.7)
+ launchy (>= 2.5.2)
+ parser (>= 3.2.2.1)
+ rainbow (~> 3.1.1)
+ reek (~> 6.0, < 7.0)
+ rexml
+ ruby_parser (~> 3.20)
+ simplecov (>= 0.22.0)
+ tty-which (~> 0.5.0)
+ virtus (~> 2.0)
rubyzip (2.3.2)
selenium-webdriver (4.9.0)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
+ sexp_processor (4.17.0)
+ simplecov (0.22.0)
+ docile (~> 1.1)
+ simplecov-html (~> 0.11)
+ simplecov_json_formatter (~> 0.1)
+ simplecov-html (0.12.3)
+ simplecov_json_formatter (0.1.4)
sprockets (4.2.1)
concurrent-ruby (~> 1.0)
rack (>= 2.2.4, < 4)
@@ -265,13 +338,19 @@ GEM
sys-uname (1.2.3)
ffi (~> 1.1)
thor (1.3.0)
+ thread_safe (0.3.6)
timeout (0.4.1)
+ tty-which (0.5.0)
turbo-rails (1.5.0)
actionpack (>= 6.0.0)
activejob (>= 6.0.0)
railties (>= 6.0.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
+ virtus (2.0.0)
+ axiom-types (~> 0.1)
+ coercible (~> 1.0)
+ descendants_tracker (~> 0.0, >= 0.0.3)
web-console (4.2.1)
actionview (>= 6.0.0)
activemodel (>= 6.0.0)
@@ -303,9 +382,16 @@ DEPENDENCIES
debug
jbuilder
jsbundling-rails
+ metric_fu-Saikuro (~> 1.1, >= 1.1.3)
puma (>= 5.0)
rails (~> 7.1.1)
+ rdoc
+ rspec (~> 3.12)
+ rspec-rails (~> 6.1)
+ rubycritic
selenium-webdriver
+ simplecov (~> 0.22.0)
+ simplecov_json_formatter (~> 0.1.4)
sprockets-rails
sqlite3 (~> 1.4)
stimulus-rails
diff --git a/app/controllers/graph_controller.rb b/app/controllers/graph_controller.rb
new file mode 100644
index 00000000..b1db89ed
--- /dev/null
+++ b/app/controllers/graph_controller.rb
@@ -0,0 +1,190 @@
+require 'json'
+
+class GraphController < ApplicationController
+ # Recebe um experimento do banco de dados,
+ # recolhe o log e retorna json com skills executadas.
+ def skillsGet
+ test_id = params[:id] # pega id da url referente ao log
+ @trials = TrialExecution.find_by(id: test_id)
+ started = false # boolean pra controle de quando começa o experimento
+ finished = false # boolean pra controle de quando termina o experimento
+
+ isNav = true # boolean para controle de destino da navigation
+ navigationList = [] # guarda info das linhas de navigation
+
+ json_data = []
+ json_id = []
+
+ # Identifica linha de navegação ("navigation") e bota no json ("json_data")
+ # "navigationList" é uma lista identificativa do destino (room ou lab)
+ #
+ # "10.06, [INFO], robot6, {'y': 18.901, 'x': -33.896, 'yaw': -3.141}, None, None"
+ # # => { "time" => 10.06, "message" => Navigation to room}
+ def navigationLine(navigation, navigationList, json_data) # referente à linha de navigation
+ time = navigation[1].to_f
+ json_data << { "time" => time, "message" => "Navigation #{navigationList[0]}"}
+ end
+
+ # Identifica linha de envio/espera de mensagem ("linhaLista") e bota no json (json_data)
+ #
+ # "86.29, [info], nurse, sync, received-request, (status=sending-request)"
+ # # => { "time" => 86.29, "message" => Sending message to nurse}
+ #
+ # "86.29, [info], nurse, sync, request-sent, (status=waiting)"
+ # # => { "time" => 86.29, "message" => Waiting the message get to nurse}
+ #
+ # "86.29, [info], nurse, sync, wait-message, (status=message-received)"
+ # # => { "time" => 86.29, "message" => Message sent to nurse}
+ def messageLine(linhaLista, json_data) # referente à linha de mensagem
+ time = linhaLista[0]
+ case
+ when linhaLista[5] == '(status=sending-request)' # request de mensagem
+ json_data << { "time" => time, "message" => "Sending message to #{linhaLista[2]}"}
+
+ when linhaLista[5] == '(status=waiting)' # espera de mensagem
+ json_data << { "time" => time, "message" => "Waiting the message get to #{linhaLista[2]}"}
+
+ when linhaLista[5] == '(status=message-received)' # mensagem recebida
+ json_data << { "time" => time, "message" => "Message sent to #{linhaLista[2]}"}
+
+ else
+
+ end
+ end
+
+ # Identifica linha de sucesso ("linhaLista") e bota no json ("json_data")
+ #
+ # "161.70, [WARN], robot6, SUCCESS, None, None"
+ # # => { "time" => 161.70, "message" => Experiment completed successfully with 161.70 seconds!}
+ def successLine(linhaLista, json_data)
+ time = linhaLista[0]
+ puts "Experiment completed successfully with #{time} seconds!"
+ json_data << { "time" => time, "message" => "Experiment completed successfully with #{time} seconds!"}
+ end
+
+ # Identifica linha de falha ("linhaLista") e bota no json ("json_data")
+ #
+ # "42.00, [WARN], robot6, NO-SKILL, authenticate_person, authenticate_nurse"
+ # # => { "time" => 42.00, "message" => Experiment failed with NO-SKILL: authenticate_person!}
+ #
+ # "281.18, [WARN], robot5, SKILL-FAILURE, navigation, navto_room"
+ # # => { "time" => 281.18, "message" => Skill navigation failed.}
+ #
+ # "49.07, [WARN], robot3, LOWBATT, None, None"
+ # # => { "time" => 49.07, "message" => Battery is low.}
+ def failureLine(linhaLista, json_data)
+ time = linhaLista[0]
+ case linhaLista[3]
+ when 'NO-SKILL'
+ json_data << { "time" => time, "message" => "Experiment failed with #{linhaLista[3]}: #{linhaLista[4]}."}
+
+ when 'SKILL-FAILURE'
+ json_data << { "time" => time, "message" => "Skill #{linhaLista[4]} failed."}
+
+ when 'LOWBATT'
+ json_data << { "time" => time, "message" => "Battery is low."}
+ end
+ end
+
+ lines = @trials.log.split("\n") # separa cada linha do log
+
+ lines.each do |line|
+ linhaLista = line.split(',').map(&:strip)
+
+ if line.include?('ROBOTS_CONFIG')
+ # posição inicial e final do hash ROBOTS_CONFIG
+ startIndex = line.index("ROBOTS_CONFIG={")
+ endIndex = line.index("}", startIndex)
+ robotsConfigStr = line[startIndex..endIndex]
+
+ # substitui aspas simples por aspas duplas
+ robotsConfigStr.gsub!("'", "\"")
+
+ # remove "ROBOTS_CONFIG=" e analisa-se como JSON
+ begin
+ robotsConfigJson = robotsConfigStr.sub("ROBOTS_CONFIG=", "")
+ robotsConfigHash = JSON.parse(robotsConfigJson)
+ rescue
+ json_data << { "message" => "TIMEOUT"}
+ break
+ end
+
+ localPlan = robotsConfigHash['local_plan']
+
+ c = 0
+ localPlan.each do |action|
+ c += 1
+ if action[0] == 'navigation'
+
+ case
+ when action[2] == 'navto_room'
+ navigationList.append('to room')
+
+ when action[2] == 'navto_lab'
+ navigationList.append('to lab')
+
+ else
+ navigationList.append('to X')
+ end
+
+ end
+ end
+
+ else
+ # montando padrões de linha
+ start = line.match(/(\d+\.\d+), \[DEBUG\], logger, Simulation open, (\w+), (\w+)/) || line.match(/(\d+\.\d+), \[INFO\], (\w+), {'y': (-?\d+\.\d+), 'x': (-?\d+\.\d+), 'yaw': (-?\d+\.\d+)}, (\w+), (\w+)/)
+ # === #
+ navigation = line.match(/(\d+\.\d+), \[INFO\], (\w+), {'battery-level': '(\d+\.\d+)'}, (\w+), (\w+)/) || line.match(/(\d+\.\d+), \[INFO\], (\w+), {'y': (-?\d+\.\d+), 'x': (-?\d+\.\d+), 'yaw': (-?\d+\.\d+)}, (\w+), (\w+)/)
+ # === #
+ message = line.match(/(\d+\.\d+), \[info\], (\w+)/)
+ # === #
+ success = line.match(/(\d+\.\d+), \[WARN\], (\w+), SUCCESS, (\w+)/)
+ # === #
+ timeout = line.match(/\[WARN\], logger, TIMEOUT, (\w+)/)
+ # === #
+ failure = line.match(/(\d+\.\d+), \[WARN\], (\w+), SKILL-FAILURE, (\w+)/) || line.match(/(\d+\.\d+), \[WARN\], (\w+), NO-SKILL, (\w+)/) || line.match(/(\d+\.\d+), \[WARN\], (\w+), LOWBATT, (\w+)/)
+ # === #
+
+
+ case
+ when navigation && started == true # caso a linha seja de navigation
+ if !isNav
+ navigationList.delete_at(0)
+ end
+ isNav = true
+ navigationLine(navigation, navigationList, json_data)
+
+ when !navigation && started == true # caso a linha não seja de navigation
+ isNav = false
+ if message # se não for navigation, pode ser de mensagem
+ messageLine(linhaLista, json_data)
+ end
+
+ if success
+ successLine(linhaLista, json_data)
+ end
+
+ if failure
+ failureLine(linhaLista, json_data)
+ end
+
+ if timeout
+ json_data << { "message" => "TIMEOUT"}
+ end
+
+ when start
+ started = true
+ json_data << { "message" => "Experiment started!"}
+
+ end
+ end
+ end
+
+
+ if @trials
+ jsonString = JSON.generate(json_data)
+ render json: jsonString
+ end
+
+ end
+end
diff --git a/app/controllers/velocity_controller.rb b/app/controllers/velocity_controller.rb
new file mode 100644
index 00000000..ba724dc9
--- /dev/null
+++ b/app/controllers/velocity_controller.rb
@@ -0,0 +1,41 @@
+require 'json'
+
+class VelocityController < ApplicationController
+ def graph
+ @teste = "teste"
+ end
+
+ #rota utilizada para adiquirir um JSON contendo as informações de velocidade média a cada período de tempo
+ def getData
+ test_id = params[:id]
+
+ #Recebe o log como string e retorna um array contendo apenas as informações de posição
+ def get_position_data(log_string)
+ output_array = []
+ log_string.split("\n").each do |line|
+ if (match = line.match(/(\d+\.\d+), \[.*?\], .*?, \{'y': (.*?), 'x': (.*?), 'yaw': (.*?)}/))
+ time, y, x, yaw = match.captures
+ output_array << { 'time' => time.to_f, 'y' => y.to_f, 'x' => x.to_f, 'yaw' => yaw.to_f }
+ end
+ end
+ output_array
+ end
+
+ #Recebe as informações de posição e retoran um JSON contendo as velocidade médias
+ def get_speed_data(positon_array)
+ output_json = {}
+ positon_array.each_cons(2) do |i, i_next|
+ output_json[i_next["time"]] = (Math.sqrt((i_next["x"] - i["x"])**2 + (i_next["y"] - i["y"])**2)/(i_next["time"]-i["time"]))
+ end
+ output_json
+ end
+
+ trial = TrialExecution.find_by(id: test_id)
+
+ if((trial == nil) || (trial.log == nil))
+ render json: {}
+ else
+ render json: get_speed_data(get_position_data(trial.log))
+ end
+ end
+end
diff --git a/app/javascript/react/src/components/Graph.jsx b/app/javascript/react/src/components/Graph.jsx
index 8ae10b09..a15d29d4 100644
--- a/app/javascript/react/src/components/Graph.jsx
+++ b/app/javascript/react/src/components/Graph.jsx
@@ -1,23 +1,144 @@
import React, {useEffect, useState} from 'react'
-
-const Graph = (props) => {
+import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend } from 'recharts';
+
+const Graph = () => {
+ const pathSegments = window.location.pathname.split('/');
+ const id = pathSegments[pathSegments.length - 1]; // pegar ID do log
- let [count, setCount] = useState(0)
+ let [req, setReq] = useState("")
useEffect(() => {
- fetch("/hello/worldGet").then(async res=>{
+ fetch(`/graph/skillsGet/${id}`).then(async res=>{
const parse = await res.text()
- console.log(parse)
return parse
}).then(res=>setReq(res))
}, [])
+
+
return (
-
Counter prop:{props.trialID}: {count}
-
+
)
-}
-
-export default Graph
+}
+
+const Component = ({ req }) => {
+ try {
+ reqArray = JSON.parse(req);
+ } catch (error) {
+ console.error('Erro ao fazer parse da string JSON:', error);
+ reqArray = [];
+ }
+
+ semRepLista = []
+ errorList = []
+ reqArray.forEach(function(key, index){
+ switch (index){
+ case 0: // primeira message tem que ser start
+ if(key.message == 'Experiment started!'){
+ semRepLista.push({"time": 0.00, "message": "Start!"})
+ }
+ else{
+ semRepLista.push({"time": 0.00, "message": "NOT STARTED!"}) // se nao, nao comecou
+ errorList.push({"time": 0.00, "message": "Error in start"})
+ }
+ break;
+
+ case reqArray.length - 1: // última message tem que ser sucesso
+ if(key.message.includes('Experiment completed successfully')){
+ semRepLista.push({"time": key.time, "message": "Success!"})
+ }
+
+ else{
+ if(key.message.includes('Skill')){ // ou falhou em alguma skill
+ errorList.push({"time": key.time, "message": key.message.substring(5)})
+ }
+
+ if(key.message.includes('Experiment failed')){ // ou faltou skill
+ errorList.push({"time": key.time, "message": key.message})
+ }
+
+ if(key.message.includes('TIMEOUT')){ // ou deu TIMEOUT
+ errorList.push({"time": reqArray[index - 1].time, "message": key.message})
+ }
+
+ if(key.message.includes('Battery')){ // ou acabou a bateria
+ errorList.push({"time": reqArray[index - 1].time, "message": key.message})
+ }
+
+ else if(errorList.length == 0){
+ errorList.push({"time": reqArray[index - 1].time, "message": 'No action taken.'})
+ }
+ }
+ break;
+
+ default:
+ if(key.time == reqArray[index+1].time && key.message == reqArray[index+1].message){
+ if(semRepLista[semRepLista.length-1].message != key.message){
+ semRepLista.push(key)
+ }
+ }
+
+ if(key.time == reqArray[index+1].time && key.message != reqArray[index+1].message){
+ if(key.message == 'Sending message to nurse' || key.message == 'Waiting the message get to nurse'){
+ if(semRepLista[semRepLista.length-1].message != 'Sending message to nurse'){
+ semRepLista.push({"time": key.time, "message":"Sending message to nurse"})
+ }
+ }
+ }
+
+ if(key.message.includes("Message sent to")){
+ if(semRepLista[semRepLista.length-1].message != key.message){
+ semRepLista.push({"time": key.time, "message":'Message sent!'})
+ }
+ }
+
+ }
+ })
+
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ {errorList.length > 0 ? (
+ errorList.map((item, index) => (
+
+
Experiment failed at {item.time} seconds: {item.message}
+
+ ))
+ ) : semRepLista.length <= 1 ? (
+
Experimento sem ações!
+ ) : (
+
Sucesso!
+ )}
+
+
+
+
+ );
+};
+
+export default Graph
\ No newline at end of file
diff --git a/app/javascript/react/src/components/Hello.jsx b/app/javascript/react/src/components/Hello.jsx
index 7aa859bf..8a75e836 100644
--- a/app/javascript/react/src/components/Hello.jsx
+++ b/app/javascript/react/src/components/Hello.jsx
@@ -1,8 +1,8 @@
import React, {useEffect, useState} from 'react'
const Hello = (props) => {
-
- let [count, setCount] = useState(2)
+
+ let [count, setCount] = useState(1)
let [req, setReq] = useState("")
useEffect(() => {
diff --git a/app/javascript/react/src/components/Velocity.jsx b/app/javascript/react/src/components/Velocity.jsx
new file mode 100644
index 00000000..9263f442
--- /dev/null
+++ b/app/javascript/react/src/components/Velocity.jsx
@@ -0,0 +1,51 @@
+import React, {useEffect, useState} from 'react'
+import { LineChart, Line, CartesianGrid, XAxis, YAxis } from 'recharts'
+
+const Velocity = () => {
+ let url = window.location.href
+ let [req, setReq] = useState("")
+
+ useEffect(() => {fetch(`/velocity/getData/${url[url.length-1]}`).then(async res=>{
+ const parse = await res.json()
+ return parse
+ }).then(res=>setReq(res))
+ }, [])
+
+
+ if (Object.keys(req).length == 0) {
+ return (
+
+
Erro
+
+ )
+
+ } else {
+
+ var lista = []
+ Object.keys(req).forEach(function(key){
+ lista.push({"x": key, "y": req[key]})
+ })
+
+
+ const data = lista; // <- lista de dicionarios
+
+ const renderLineChart = (
+
+
+
+ );
+ console.log(lista)
+ return (
+
+
Gráfico de velocidado média em determinados tempos:
+
+
+
+
+
+
+
+ )
+ }
+}
+export default Velocity
diff --git a/app/javascript/react/src/index.js b/app/javascript/react/src/index.js
index a9759c53..ef644e8c 100644
--- a/app/javascript/react/src/index.js
+++ b/app/javascript/react/src/index.js
@@ -1,5 +1,6 @@
import { define } from 'remount'
import Hello from "./components/Hello"
-import Graph from "./components/Graph"
+import Velocity from "./components/Velocity"
+import Graph from './components/Graph'
-define({ 'hello-component': Hello, 'graph-component': Graph })
+define({ 'hello-component': Hello, 'velocity-component': Velocity, 'graph-component': Graph })
diff --git a/app/views/graph/skills.html.erb b/app/views/graph/skills.html.erb
new file mode 100644
index 00000000..07d83a11
--- /dev/null
+++ b/app/views/graph/skills.html.erb
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/views/hello/world.html.erb b/app/views/hello/world.html.erb
index 2be2c312..c5d912c1 100644
--- a/app/views/hello/world.html.erb
+++ b/app/views/hello/world.html.erb
@@ -1 +1 @@
-
+
diff --git a/app/views/velocity/getData.html.erb b/app/views/velocity/getData.html.erb
new file mode 100644
index 00000000..271ce884
--- /dev/null
+++ b/app/views/velocity/getData.html.erb
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/views/velocity/graph.html.erb b/app/views/velocity/graph.html.erb
new file mode 100644
index 00000000..271ce884
--- /dev/null
+++ b/app/views/velocity/graph.html.erb
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/bun.lockb b/bun.lockb
index fbc672dd..b982f8da 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/config/routes.rb b/config/routes.rb
index a27857a6..32994182 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,6 +1,11 @@
Rails.application.routes.draw do
get 'hello/world'
get 'hello/worldGet', to: "hello#index"
+ get 'graph/skills/:id', to: "graph#skills"
+ get 'graph/skillsGet/:id', to: "graph#skillsGet"
+ get 'velocity/graph/:id', to: "velocity#graph"
+ get 'velocity/getData/:id', to: "velocity#getData"
+
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
# Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
diff --git a/db/schema.rb b/db/schema.rb
index 183d56d5..6763f89a 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -44,8 +44,10 @@
create_table "trial_executions", force: :cascade do |t|
t.string "status"
t.text "log"
+ t.integer "trial_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["trial_id"], name: "index_trial_executions_on_trial_id"
end
create_table "trial_factors", force: :cascade do |t|
diff --git a/db/seeds.rb b/db/seeds.rb
index 4fbd6ed9..7294e696 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -7,3 +7,55 @@
# ["Action", "Comedy", "Drama", "Horror"].each do |genre_name|
# MovieGenre.find_or_create_by!(name: genre_name)
# end
+
+def create_example_data()
+ Experiment.create([ name: "teste", disabled: false ])
+ Trial.create([name: "testeTrial", disabled: false, deleted: false, runs: 20, experiment_id: Experiment.first.id])
+ TrialExecution.create(status: "bom", log:"10.02, [INFO], robot4, {'battery-level': '17.26'}, None, None
+10.02, [INFO], robot4, {'y': 34.57, 'x': -38.121, 'yaw': -3.142}, None, None
+19.99, [INFO], robot4, {'battery-level': '17.00'}, None, None
+19.99, [INFO], robot4, {'y': 33.238, 'x': -37.019, 'yaw': 3.141}, None, None
+30.01, [INFO], robot4, {'battery-level': '16.74'}, None, None
+30.01, [INFO], robot4, {'y': 31.112, 'x': -37.069, 'yaw': 3.141}, None, None
+39.97, [INFO], robot4, {'y': 28.987, 'x': -37.012, 'yaw': 3.14}, None, None
+39.97, [INFO], robot4, {'battery-level': '16.48'}, None, None
+49.98, [INFO], robot4, {'battery-level': '16.22'}, None, None
+50.01, [INFO], robot4, {'y': 26.87, 'x': -37.012, 'yaw': 3.142}, None, None
+60.02, [INFO], robot4, {'battery-level': '15.96'}, None, None
+60.02, [INFO], robot4, {'y': 24.742, 'x': -37.047, 'yaw': 3.142}, None, None
+69.98, [INFO], robot4, {'y': 22.63, 'x': -37.066, 'yaw': 3.141}, None, None
+70.00, [INFO], robot4, {'battery-level': '15.70'}, None, None
+79.99, [INFO], robot4, {'battery-level': '15.44'}, None, None
+79.99, [INFO], robot4, {'y': 20.504, 'x': -37.147, 'yaw': 3.142}, None, None
+90.02, [INFO], robot4, {'y': 18.53, 'x': -36.598, 'yaw': 3.141}, None, None
+90.02, [INFO], robot4, {'battery-level': '15.18'}, None, None
+100.01, [INFO], robot4, {'battery-level': '14.92'}, None, None
+100.01, [INFO], robot4, {'y': 17.763, 'x': -34.786, 'yaw': -3.142}, None, None
+109.99, [INFO], robot4, {'y': 18.934, 'x': -33.913, 'yaw': 3.142}, None, None
+109.99, [INFO], robot4, {'battery-level': '14.66'}, None, None
+113.74, [info], nurse, sync, received-request, (status=sending-request)
+113.74, [info], nurse, sync, request-sent, (status=waiting)
+113.75, [info], nurse, sync, wait-message, (status=message-received)
+120.00, [INFO], robot4, {'y': 18.079, 'x': -34.685, 'yaw': -3.142}, None, None
+120.00, [INFO], robot4, {'battery-level': '14.40'}, None, None
+129.99, [INFO], robot4, {'battery-level': '14.14'}, None, None
+130.00, [INFO], robot4, {'y': 18.038, 'x': -36.106, 'yaw': -3.142}, None, None
+140.02, [INFO], robot4, {'battery-level': '13.88'}, None, None
+140.02, [INFO], robot4, {'y': 18.081, 'x': -36.168, 'yaw': 3.142}, None, None
+149.98, [INFO], robot4, {'y': 17.575, 'x': -37.036, 'yaw': 3.142}, None, None
+149.98, [INFO], robot4, {'battery-level': '13.62'}, None, None
+160.01, [INFO], robot4, {'y': 17.345, 'x': -36.954, 'yaw': -3.141}, None, None
+160.01, [INFO], robot4, {'battery-level': '13.36'}, None, None
+170.01, [INFO], robot4, {'y': 17.274, 'x': -37.095, 'yaw': -3.141}, None, None
+170.01, [INFO], robot4, {'battery-level': '13.10'}, None, None
+179.97, [INFO], robot4, {'battery-level': '12.84'}, None, None
+180.00, [INFO], robot4, {'y': 17.402, 'x': -37.034, 'yaw': -3.142}, None, None
+190.01, [INFO], robot4, {'y': 17.205, 'x': -36.812, 'yaw': -3.141}, None, None
+190.01, [INFO], robot4, {'battery-level': '12.58'}, None, None
+200.02, [INFO], robot4, {'battery-level': '12.32'}, None, None
+200.02, [INFO], robot4, {'y': 17.363, 'x': -36.885, 'yaw': 3.142}, None, None
+210.01, [INFO], robot4, {'y': 17.131, 'x': -37.113, 'yaw': 3.142}, None, None
+210.01, [INFO], robot4, {'battery-level': '12.06'}, None, None", trial_id: Trial.find_by(id: "1").id)
+end
+
+#create_example_data()
\ No newline at end of file
diff --git a/features/skills.feature b/features/skills.feature
new file mode 100644
index 00000000..8cf86a85
--- /dev/null
+++ b/features/skills.feature
@@ -0,0 +1,27 @@
+Feature: View skills data from the test run
+ As a user interested in the results of tests performed,
+ To understand the skills demonstrated during a test,
+ I want to visualize the skills data associated with the robot.
+
+Context:
+ Given that I have run a test in the simulator,
+ Then I should see the robot's skills in the test run.
+
+@javascript
+Scenario: Failure
+ Given that the robot has different skills in its local plan,
+ And the progress of the plan depends on the success of each skill,
+ Then it must be informed which of the robot's skills failed.
+
+
+@javascript
+Scenario: Success
+ Given that a robot has a set of skills
+ And all skills are executed with success
+ Then the interface should show the graph of the skills
+
+@javascript
+Scenario: Robot without skills
+ Given that a robot is selected for skills data visualization,
+ And this robot has no skills assigned,
+ Then the interface should indicate that there are no skills available for the robot.
diff --git a/features/speed.feature b/features/speed.feature
new file mode 100644
index 00000000..dbe212d9
--- /dev/null
+++ b/features/speed.feature
@@ -0,0 +1,30 @@
+Feature: View speed data
+ As a user I want to be able to view speed data from the test run
+
+@javascript
+Scenario: Test not run (Sad)
+ Given that the user is on a experiment screen
+ And the test was never run
+ When I click on the speed button
+ Then it shouldn't be possible to view the data
+
+@javascript
+Scenario: Test completed successfully (Happy)
+ Given that the user is on a experiment screen
+ And the test was run successfully
+ When I click on the speed button
+ Then I should see in a graph the speed information that was collected during the test run
+
+@javascript
+Scenario: Test completed with failure (Sad)
+ Given that the user is on a experiment screen
+ And the test ran successfully but with a failure
+ When I click on the speed button
+ Then I should see the speed information until the point of failure
+
+@javascript
+Scenario: Data collection failed (Sad)
+ Given that the user is on a experiment screen
+ And that the test was run but the speed information is missing
+ When I click on the speed button
+ Then the application should report an error
diff --git a/features/step_definitions/skills_step.rb b/features/step_definitions/skills_step.rb
new file mode 100644
index 00000000..0e45b1ea
--- /dev/null
+++ b/features/step_definitions/skills_step.rb
@@ -0,0 +1,85 @@
+Given("that the robot has different skills in its local plan,") do
+end
+
+And ("the progress of the plan depends on the success of each skill,") do
+ Experiment.create([ name: "teste", disabled: false ])
+ Trial.create([name: "testeTrial", disabled: false, deleted: false, runs: 20, experiment_id: Experiment.find_by(id: "1").id])
+ TrialExecution.create(status: "bom", log: "0.00, [INFO], robot3, {'battery-level': '8.04'}, None, None
+ 0.12, [DEBUG], logger, ROBOTS_CONFIG={'avg_speed': 0.15, 'battery_charge': 0.08039941318304296, 'battery_discharge_rate': 0.00064, 'id': 3, 'local_plan': [['navigation', ['IC Room 6', [[-38.0, 21.5, 0.0], [-37.0, 21.5], [-37.0, 18.93], [-33.9, 18.93, 3.14]]], 'navto_room'], ['approach_person', ['nurse'], 'approach_nurse'], ['authenticate_person', ['nurse'], 'authenticate_nurse'], ['operate_drawer', ['open'], 'open_drawer_for_nurse'], ['send_message', ['nurse'], 'notify_nurse_of_open_drawer_for_nurse_completed'], ['wait_message', ['nurse'], 'wait_nurse_to_complete_deposit'], ['operate_drawer', ['close'], 'close_drawer_nurse'], ['navigation', ['Laboratory', [[-33.9, 18.93, 3.14], [-37.0, 18.93], [-37.0, 16.0], [-26.0, 16.0], [-26.0, 13.0, 1.57]]], 'navto_lab'], ['approach_robot', ['lab_arm'], 'approach_arm'], ['operate_drawer', ['open'], 'open_drawer_lab'], ['send_message', ['lab_arm'], 'notify_lab_arm_of_open_drawer_lab_completed'], ['wait_message', ['lab_arm'], 'wait_lab_arm_to_complete_pick_up_sample'], ['operate_drawer', ['close'], 'close_drawer_lab']], 'location': 'IC Room 2', 'name': 'r3', 'position': [-38.0, 21.5, 0.0], 'skills': ['approach_person', 'approach_robot', 'navigation', 'operate_drawer']}, None, None
+ 0.12, [DEBUG], logger, Simulation open, None, None
+ 10.05, [INFO], robot3, {'y': 21.423, 'x': -37.237, 'yaw': -3.141}, None, None
+ 10.05, [INFO], robot3, {'battery-level': '7.46'}, None, None
+ 20.08, [INFO], robot3, {'y': 19.318, 'x': -37.061, 'yaw': 3.139}, None, None
+ 20.08, [INFO], robot3, {'battery-level': '6.82'}, None, None
+ 30.06, [INFO], robot3, {'battery-level': '6.18'}, None, None
+ 30.06, [INFO], robot3, {'y': 17.825, 'x': -35.564, 'yaw': -3.142}, None, None
+ 40.03, [INFO], robot3, {'battery-level': '5.54'}, None, None
+ 40.03, [INFO], robot3, {'y': 18.858, 'x': -34.0, 'yaw': -3.142}, None, None
+ 46.90, [WARN], robot3, NO-SKILL, authenticate_person, authenticate_nurse", trial_id: Trial.find_by(id: "1").id)
+end
+
+Then("it must be informed which of the robot's skills failed.") do
+ visit '/graph/skills/1'
+ page.should have_selector('p', text: 'Experiment failed ')
+
+end
+
+Given ("that a robot is selected for skills data visualization,") do
+
+end
+
+And ("this robot has no skills assigned,") do
+ Experiment.create([ name: "teste", disabled: false ])
+ Trial.create([name: "testeTrial", disabled: false, deleted: false, runs: 20, experiment_id: Experiment.find_by(id: "1").id])
+ TrialExecution.create(status: "bom", log: "0.00, [DEBUG], nurse, NURSE_CONFIG={'location': 'PC Room 3', 'position': [-28.5, 18.0, -1.57]}, None, None
+ 0.00, [INFO], robot1, {'battery-level': '24.00'}, None, None
+ 0.19, [DEBUG], logger, ROBOTS_CONFIG={'avg_speed': 0.15, 'battery_charge': 0.23995139509475963, 'battery_discharge_rate': 0.0006600000000000001, 'id': 1, 'local_plan': [['navigation', ['PC Room 3', [[-39.44, 33.95, 0.0], [-37.0, 33.95], [-37.0, 16.0], [-28.5, 16.0], [-28.5, 18.0, -1.57]]], 'navto_room'], ['approach_person', ['nurse'], 'approach_nurse'], ['authenticate_person', ['nurse'], 'authenticate_nurse'], ['operate_drawer', ['open'], 'open_drawer_for_nurse'], ['send_message', ['nurse'], 'notify_nurse_of_open_drawer_for_nurse_completed'], ['wait_message', ['nurse'], 'wait_nurse_to_complete_deposit'], ['operate_drawer', ['close'], 'close_drawer_nurse'], ['navigation', ['Laboratory', [[-28.5, 18.0, -1.57], [-28.5, 16.0], [-26.0, 16.0], [-26.0, 13.0, 1.57]]], 'navto_lab'], ['approach_robot', ['lab_arm'], 'approach_arm'], ['operate_drawer', ['open'], 'open_drawer_lab'], ['send_message', ['lab_arm'], 'notify_lab_arm_of_open_drawer_lab_completed'], ['wait_message', ['lab_arm'], 'wait_lab_arm_to_complete_pick_up_sample'], ['operate_drawer', ['close'], 'close_drawer_lab']], 'location': 'IC Room 4', 'name': 'r1', 'position': [-39.44, 33.95, 0.0], 'skills': ['approach_person', 'approach_robot', 'navigation', 'operate_drawer']}, None, None
+ 0.19, [DEBUG], logger, Simulation open, None, None", trial_id: Trial.find_by(id: "1").id)
+end
+
+Then ("the interface should indicate that there are no skills available for the robot.") do
+ visit '/graph/skills/1'
+ page.should have_selector('p', text: 'Experimento sem ações!')
+end
+
+Given('that a robot has a set of skills') do
+end
+
+Given('all skills are executed with success') do
+ Experiment.create([ name: "teste", disabled: false ])
+ Trial.create([name: "testeTrial", disabled: false, deleted: false, runs: 20, experiment_id: Experiment.find_by(id: "1").id])
+ TrialExecution.create(status: "bom", log: "0.19, [DEBUG], logger, ROBOTS_CONFIG={'avg_speed': 0.15, 'battery_charge': 0.5779176354739738, 'battery_discharge_rate': 0.0006000000000000001, 'id': 2, 'local_plan': [['navigation', ['PC Room 3', [[-21.0, 18.0, -1.57], [-21.0, 16.0], [-28.5, 16.0], [-28.5, 18.0, -1.57]]], 'navto_room'], ['approach_person', ['nurse'], 'approach_nurse'], ['authenticate_person', ['nurse'], 'authenticate_nurse'], ['operate_drawer', ['open'], 'open_drawer_for_nurse'], ['send_message', ['nurse'], 'notify_nurse_of_open_drawer_for_nurse_completed'], ['wait_message', ['nurse'], 'wait_nurse_to_complete_deposit'], ['operate_drawer', ['close'], 'close_drawer_nurse'], ['navigation', ['Laboratory', [[-28.5, 18.0, -1.57], [-28.5, 16.0], [-26.0, 16.0], [-26.0, 13.0, 1.57]]], 'navto_lab'], ['approach_robot', ['lab_arm'], 'approach_arm'], ['operate_drawer', ['open'], 'open_drawer_lab'], ['send_message', ['lab_arm'], 'notify_lab_arm_of_open_drawer_lab_completed'], ['wait_message', ['lab_arm'], 'wait_lab_arm_to_complete_pick_up_sample'], ['operate_drawer', ['close'], 'close_drawer_lab']], 'location': 'PC Room 5', 'name': 'r2', 'position': [-21.0, 18.0, -1.57], 'skills': ['approach_person', 'approach_robot', 'authenticate_person', 'navigation', 'operate_drawer']}, None, None
+ 0.19, [DEBUG], logger, Simulation open, None, None
+ 10.07, [INFO], robot2, {'y': 17.533, 'x': -20.967, 'yaw': 3.142}, None, None
+ 10.07, [INFO], robot2, {'battery-level': '57.25'}, None, None
+ 20.02, [INFO], robot2, {'y': 16.111, 'x': -21.741, 'yaw': -3.141}, None, None
+ 20.02, [INFO], robot2, {'battery-level': '56.65'}, None, None
+ 30.07, [INFO], robot2, {'battery-level': '56.05'}, None, None
+ 30.07, [INFO], robot2, {'y': 16.007, 'x': -23.865, 'yaw': 3.141}, None, None
+ 40.07, [INFO], robot2, {'battery-level': '55.45'}, None, None
+ 40.07, [INFO], robot2, {'y': 15.987, 'x': -25.987, 'yaw': 3.139}, None, None
+ 50.01, [INFO], robot2, {'battery-level': '54.85'}, None, None
+ 50.01, [INFO], robot2, {'y': 16.048, 'x': -28.101, 'yaw': 3.141}, None, None
+ 60.06, [INFO], robot2, {'y': 17.831, 'x': -28.598, 'yaw': 3.14}, None, None
+ 60.06, [INFO], robot2, {'battery-level': '54.25'}, None, None
+ 70.01, [INFO], robot2, {'y': 18.047, 'x': -28.577, 'yaw': 3.142}, None, None
+ 70.01, [INFO], robot2, {'battery-level': '53.65'}, None, None
+ 74.05, [info], nurse, sync, received-request, (status=sending-request)
+ 74.05, [info], nurse, sync, request-sent, (status=waiting)
+ 74.05, [info], nurse, sync, wait-message, (status=message-received)
+ 80.09, [INFO], robot2, {'battery-level': '53.05'}, None, None
+ 80.09, [INFO], robot2, {'y': 17.0, 'x': -28.698, 'yaw': 3.141}, None, None
+ 90.06, [INFO], robot2, {'battery-level': '52.45'}, None, None
+ 90.06, [INFO], robot2, {'y': 16.105, 'x': -27.307, 'yaw': -3.142}, None, None
+ 100.04, [INFO], robot2, {'battery-level': '51.85'}, None, None
+ 100.04, [INFO], robot2, {'y': 14.956, 'x': -25.939, 'yaw': 3.141}, None, None
+ 110.06, [INFO], robot2, {'battery-level': '51.25'}, None, None
+ 110.06, [INFO], robot2, {'y': 12.981, 'x': -25.854, 'yaw': 3.142}, None, None
+ 118.90, [info], lab_arm, sync, wait-message, (status=message-received)
+ 119.82, [WARN], robot2, SUCCESS, None, None", trial_id: Trial.find_by(id: "1").id)
+end
+
+Then('the interface should show the graph of the skills') do
+ visit '/graph/skills/1'
+ page.should have_selector('p', text: 'Sucesso!')
+end
\ No newline at end of file
diff --git a/features/step_definitions/speed_step.rb b/features/step_definitions/speed_step.rb
new file mode 100644
index 00000000..b34ec572
--- /dev/null
+++ b/features/step_definitions/speed_step.rb
@@ -0,0 +1,104 @@
+Given("that the user is on a experiment screen") do
+ #do nothing
+end
+
+And ("the test was never run") do
+ Experiment.create([ name: "teste", disabled: false ])
+ Trial.create([name: "testeTrial", disabled: false, deleted: false, runs: 20, experiment_id: Experiment.first.id])
+ #Não existe trial execution
+end
+
+When("I click on the speed button") do
+ visit "/velocity/graph/1" #sempre tem id 1 por causa da maneira como o cucumber funciona
+end
+
+Then("it shouldn't be possible to view the data") do
+ expect(page).to have_content("Erro")
+end
+
+
+And("the test was run successfully") do
+ Experiment.create([ name: "teste", disabled: false ])
+ Trial.create([name: "testeTrial", disabled: false, deleted: false, runs: 20, experiment_id: Experiment.first.id])
+ TrialExecution.create(status: "bom", log:"10.02, [INFO], robot4, {'battery-level': '17.26'}, None, None
+10.02, [INFO], robot4, {'y': 34.57, 'x': -38.121, 'yaw': -3.142}, None, None
+19.99, [INFO], robot4, {'battery-level': '17.00'}, None, None
+19.99, [INFO], robot4, {'y': 33.238, 'x': -37.019, 'yaw': 3.141}, None, None
+30.01, [INFO], robot4, {'battery-level': '16.74'}, None, None
+30.01, [INFO], robot4, {'y': 31.112, 'x': -37.069, 'yaw': 3.141}, None, None
+39.97, [INFO], robot4, {'y': 28.987, 'x': -37.012, 'yaw': 3.14}, None, None
+39.97, [INFO], robot4, {'battery-level': '16.48'}, None, None
+49.98, [INFO], robot4, {'battery-level': '16.22'}, None, None
+50.01, [INFO], robot4, {'y': 26.87, 'x': -37.012, 'yaw': 3.142}, None, None
+60.02, [INFO], robot4, {'battery-level': '15.96'}, None, None
+60.02, [INFO], robot4, {'y': 24.742, 'x': -37.047, 'yaw': 3.142}, None, None
+69.98, [INFO], robot4, {'y': 22.63, 'x': -37.066, 'yaw': 3.141}, None, None
+70.00, [INFO], robot4, {'battery-level': '15.70'}, None, None
+79.99, [INFO], robot4, {'battery-level': '15.44'}, None, None
+79.99, [INFO], robot4, {'y': 20.504, 'x': -37.147, 'yaw': 3.142}, None, None
+90.02, [INFO], robot4, {'y': 18.53, 'x': -36.598, 'yaw': 3.141}, None, None
+90.02, [INFO], robot4, {'battery-level': '15.18'}, None, None
+100.01, [INFO], robot4, {'battery-level': '14.92'}, None, None
+100.01, [INFO], robot4, {'y': 17.763, 'x': -34.786, 'yaw': -3.142}, None, None
+109.99, [INFO], robot4, {'y': 18.934, 'x': -33.913, 'yaw': 3.142}, None, None
+109.99, [INFO], robot4, {'battery-level': '14.66'}, None, None
+113.74, [info], nurse, sync, received-request, (status=sending-request)
+113.74, [info], nurse, sync, request-sent, (status=waiting)
+113.75, [info], nurse, sync, wait-message, (status=message-received)
+120.00, [INFO], robot4, {'y': 18.079, 'x': -34.685, 'yaw': -3.142}, None, None
+120.00, [INFO], robot4, {'battery-level': '14.40'}, None, None
+129.99, [INFO], robot4, {'battery-level': '14.14'}, None, None
+130.00, [INFO], robot4, {'y': 18.038, 'x': -36.106, 'yaw': -3.142}, None, None
+140.02, [INFO], robot4, {'battery-level': '13.88'}, None, None
+140.02, [INFO], robot4, {'y': 18.081, 'x': -36.168, 'yaw': 3.142}, None, None
+149.98, [INFO], robot4, {'y': 17.575, 'x': -37.036, 'yaw': 3.142}, None, None
+149.98, [INFO], robot4, {'battery-level': '13.62'}, None, None
+160.01, [INFO], robot4, {'y': 17.345, 'x': -36.954, 'yaw': -3.141}, None, None
+160.01, [INFO], robot4, {'battery-level': '13.36'}, None, None
+170.01, [INFO], robot4, {'y': 17.274, 'x': -37.095, 'yaw': -3.141}, None, None
+170.01, [INFO], robot4, {'battery-level': '13.10'}, None, None
+179.97, [INFO], robot4, {'battery-level': '12.84'}, None, None
+180.00, [INFO], robot4, {'y': 17.402, 'x': -37.034, 'yaw': -3.142}, None, None
+190.01, [INFO], robot4, {'y': 17.205, 'x': -36.812, 'yaw': -3.141}, None, None
+190.01, [INFO], robot4, {'battery-level': '12.58'}, None, None
+200.02, [INFO], robot4, {'battery-level': '12.32'}, None, None
+200.02, [INFO], robot4, {'y': 17.363, 'x': -36.885, 'yaw': 3.142}, None, None
+210.01, [INFO], robot4, {'y': 17.131, 'x': -37.113, 'yaw': 3.142}, None, None
+210.01, [INFO], robot4, {'battery-level': '12.06'}, None, None", trial_id: Trial.find_by(id: "1").id)
+end
+
+
+Then("I should see in a graph the speed information that was collected during the test run") do
+ page.should have_css('div.recharts-wrapper')
+end
+
+Then("I should see the speed information until the point of failure") do
+ page.should have_css('div.recharts-wrapper')
+end
+
+Then("the application should report an error") do
+ expect(page).to have_content("Erro")
+end
+
+And("the test ran successfully but with a failure") do
+ Experiment.create([ name: "teste", disabled: false ])
+ Trial.create([name: "testeTrial", disabled: false, deleted: false, runs: 20, experiment_id: Experiment.first.id])
+ TrialExecution.create(status: "bom", log:"0.00, [INFO], robot3, {'battery-level': '8.04'}, None, None
+ 0.12, [DEBUG], logger, ROBOTS_CONFIG={'avg_speed': 0.15, 'battery_charge': 0.08039941318304296, 'battery_discharge_rate': 0.00064, 'id': 3, 'local_plan': [['navigation', ['IC Room 6', [[-38.0, 21.5, 0.0], [-37.0, 21.5], [-37.0, 18.93], [-33.9, 18.93, 3.14]]], 'navto_room'], ['approach_person', ['nurse'], 'approach_nurse'], ['authenticate_person', ['nurse'], 'authenticate_nurse'], ['operate_drawer', ['open'], 'open_drawer_for_nurse'], ['send_message', ['nurse'], 'notify_nurse_of_open_drawer_for_nurse_completed'], ['wait_message', ['nurse'], 'wait_nurse_to_complete_deposit'], ['operate_drawer', ['close'], 'close_drawer_nurse'], ['navigation', ['Laboratory', [[-33.9, 18.93, 3.14], [-37.0, 18.93], [-37.0, 16.0], [-26.0, 16.0], [-26.0, 13.0, 1.57]]], 'navto_lab'], ['approach_robot', ['lab_arm'], 'approach_arm'], ['operate_drawer', ['open'], 'open_drawer_lab'], ['send_message', ['lab_arm'], 'notify_lab_arm_of_open_drawer_lab_completed'], ['wait_message', ['lab_arm'], 'wait_lab_arm_to_complete_pick_up_sample'], ['operate_drawer', ['close'], 'close_drawer_lab']], 'location': 'IC Room 2', 'name': 'r3', 'position': [-38.0, 21.5, 0.0], 'skills': ['approach_person', 'approach_robot', 'navigation', 'operate_drawer']}, None, None
+ 0.12, [DEBUG], logger, Simulation open, None, None
+ 10.05, [INFO], robot3, {'y': 21.423, 'x': -37.237, 'yaw': -3.141}, None, None
+ 10.05, [INFO], robot3, {'battery-level': '7.46'}, None, None
+ 20.08, [INFO], robot3, {'y': 19.318, 'x': -37.061, 'yaw': 3.139}, None, None
+ 20.08, [INFO], robot3, {'battery-level': '6.82'}, None, None
+ 30.06, [INFO], robot3, {'battery-level': '6.18'}, None, None
+ 30.06, [INFO], robot3, {'y': 17.825, 'x': -35.564, 'yaw': -3.142}, None, None
+ 40.03, [INFO], robot3, {'battery-level': '5.54'}, None, None
+ 40.03, [INFO], robot3, {'y': 18.858, 'x': -34.0, 'yaw': -3.142}, None, None
+ 46.90, [WARN], robot3, NO-SKILL, authenticate_person, authenticate_nurse", trial_id: Trial.find_by(id: "1").id)
+end
+
+And("that the test was run but the speed information is missing") do
+ Experiment.create([ name: "teste", disabled: false ])
+ Trial.create([name: "testeTrial", disabled: false, deleted: false, runs: 20, experiment_id: Experiment.first.id])
+ TrialExecution.create(status: "bom", trial_id: Trial.find_by(id: "1").id)
+end
diff --git a/features/support/env.rb b/features/support/env.rb
index aa0a0b73..336c8a7d 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -80,4 +80,9 @@
config.server_port = '3000'
end
-Capybara.javascript_driver = :selenium_remote_chrome
\ No newline at end of file
+Capybara.register_driver :chrome_headless do |app|
+ Capybara::Selenium::Driver.new app, browser: :chrome,
+ options: Selenium::WebDriver::Chrome::Options.new(args: %w[headless disable-gpu])
+end
+
+Capybara.javascript_driver = :chrome_headless
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..bd023e0b
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,525 @@
+{
+ "name": "app",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "app",
+ "dependencies": {
+ "@hotwired/stimulus": "^3.2.2",
+ "@hotwired/turbo-rails": "^7.3.0",
+ "bun": "^1.0.18",
+ "bundle": "^2.1.0",
+ "chart.js": "^4.4.1",
+ "react": "^18.2.0",
+ "react-chartjs-2": "^5.2.0",
+ "react-dom": "^18.2.0",
+ "recharts": "^2.10.3",
+ "remount": "^1.0.0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.23.5",
+ "license": "MIT",
+ "dependencies": {
+ "regenerator-runtime": "^0.14.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@hotwired/stimulus": {
+ "version": "3.2.2",
+ "license": "MIT"
+ },
+ "node_modules/@hotwired/turbo": {
+ "version": "7.3.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/@hotwired/turbo-rails": {
+ "version": "7.3.0",
+ "license": "MIT",
+ "dependencies": {
+ "@hotwired/turbo": "^7.3.0",
+ "@rails/actioncable": "^7.0"
+ }
+ },
+ "node_modules/@kurkle/color": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
+ "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
+ },
+ "node_modules/@oven/bun-darwin-aarch64": {
+ "version": "1.0.18",
+ "resolved": "https://registry.npmjs.org/@oven/bun-darwin-aarch64/-/bun-darwin-aarch64-1.0.18.tgz",
+ "integrity": "sha512-lXTdQpajIxP6cSAl4cvdKGVFrjPWKRVrK+PfvlX5Ub/fhyve8nKc5u1BcBG9448QqJ7xCD94bHFDgHxj3kwSmQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@oven/bun-darwin-x64": {
+ "version": "1.0.18",
+ "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64/-/bun-darwin-x64-1.0.18.tgz",
+ "integrity": "sha512-WHdTNKjFq7n8RwDPQj6BSop/uDYMpTKU+DY6VzSo0Qv+Dx3n00TaWSdexGrJZfAaPD2Z1GWVuJsGlCJ6nFYehQ==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@oven/bun-darwin-x64-baseline": {
+ "version": "1.0.18",
+ "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64-baseline/-/bun-darwin-x64-baseline-1.0.18.tgz",
+ "integrity": "sha512-13Hkg2vfK6blR63OCJZxJGPPsozeQEP7djawopQBS/StWPxL7Ph+kLIqja7MUtgRC/n/t8mBDM4V8c8KYrursQ==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@oven/bun-linux-aarch64": {
+ "version": "1.0.18",
+ "resolved": "https://registry.npmjs.org/@oven/bun-linux-aarch64/-/bun-linux-aarch64-1.0.18.tgz",
+ "integrity": "sha512-TA4QG8nWXsnluwyi0tnzbTj0lpI18Nl/VZfuwUGfaXyRT6MSTnhhsdPu8nPKNvN6dtzfJLkKxejH8dw7FyfwPw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@oven/bun-linux-x64": {
+ "version": "1.0.18",
+ "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64/-/bun-linux-x64-1.0.18.tgz",
+ "integrity": "sha512-4quR8sGCmE4XwH+tCnpgVFpO/VwDzaLGbXB3bZ7dOy9yp3UjDVVMbgq3kUAZwR915cjC9bvS0ham2E9QyFQMYw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@oven/bun-linux-x64-baseline": {
+ "version": "1.0.18",
+ "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-baseline/-/bun-linux-x64-baseline-1.0.18.tgz",
+ "integrity": "sha512-mfud9gyQI3dTxT7aTqUzCICQeJ6BoHfyDjyqo95v42whAhXL0C5HVDBSLjoBBIeLirJsqXoVCOnCZILFWS1Zrw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rails/actioncable": {
+ "version": "7.1.1",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-array": {
+ "version": "3.2.1",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-color": {
+ "version": "3.1.3",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-ease": {
+ "version": "3.0.2",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-interpolate": {
+ "version": "3.0.4",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-color": "*"
+ }
+ },
+ "node_modules/@types/d3-path": {
+ "version": "3.0.2",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-scale": {
+ "version": "4.0.8",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-time": "*"
+ }
+ },
+ "node_modules/@types/d3-shape": {
+ "version": "3.1.6",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-path": "*"
+ }
+ },
+ "node_modules/@types/d3-time": {
+ "version": "3.0.3",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-timer": {
+ "version": "3.0.2",
+ "license": "MIT"
+ },
+ "node_modules/bun": {
+ "version": "1.0.18",
+ "resolved": "https://registry.npmjs.org/bun/-/bun-1.0.18.tgz",
+ "integrity": "sha512-l0GiCckLxetsUmZwiJCgXor6NCvZVXg+Avk9mVb1JmIukm2+4e5HZWpelVwjVzPLmAHlDv2VyoCx4OX+XiWKgA==",
+ "cpu": [
+ "arm64",
+ "x64"
+ ],
+ "hasInstallScript": true,
+ "os": [
+ "darwin",
+ "linux"
+ ],
+ "bin": {
+ "bun": "bin/bun",
+ "bunx": "bin/bun"
+ },
+ "optionalDependencies": {
+ "@oven/bun-darwin-aarch64": "1.0.18",
+ "@oven/bun-darwin-x64": "1.0.18",
+ "@oven/bun-darwin-x64-baseline": "1.0.18",
+ "@oven/bun-linux-aarch64": "1.0.18",
+ "@oven/bun-linux-x64": "1.0.18",
+ "@oven/bun-linux-x64-baseline": "1.0.18"
+ }
+ },
+ "node_modules/bundle": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/bundle/-/bundle-2.1.0.tgz",
+ "integrity": "sha512-d7TeT8m2HuymDjSEmMppWe/h5SSPPUZkaWKrAofx6gNXDdZ3FL/81oOTGPG+LIaZbNr9m4rtUi98Yw0Q1vHIIw=="
+ },
+ "node_modules/chart.js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.1.tgz",
+ "integrity": "sha512-C74QN1bxwV1v2PEujhmKjOZ7iUM4w6BWs23Md/6aOZZSlwMzeCIDGuZay++rBgChYru7/+QFeoQW0fQoP534Dg==",
+ "dependencies": {
+ "@kurkle/color": "^0.3.0"
+ },
+ "engines": {
+ "pnpm": ">=7"
+ }
+ },
+ "node_modules/clsx": {
+ "version": "2.0.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/d3-array": {
+ "version": "3.2.4",
+ "license": "ISC",
+ "dependencies": {
+ "internmap": "1 - 2"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-color": {
+ "version": "3.1.0",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-ease": {
+ "version": "3.0.1",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-format": {
+ "version": "3.1.0",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-interpolate": {
+ "version": "3.0.1",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-path": {
+ "version": "3.1.0",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale": {
+ "version": "4.0.2",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-shape": {
+ "version": "3.2.0",
+ "license": "ISC",
+ "dependencies": {
+ "d3-path": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time": {
+ "version": "3.1.0",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time-format": {
+ "version": "4.1.0",
+ "license": "ISC",
+ "dependencies": {
+ "d3-time": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-timer": {
+ "version": "3.0.1",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/decimal.js-light": {
+ "version": "2.5.1",
+ "license": "MIT"
+ },
+ "node_modules/dom-helpers": {
+ "version": "3.4.0",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.1.2"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "4.0.7",
+ "license": "MIT"
+ },
+ "node_modules/fast-equals": {
+ "version": "5.0.1",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/internmap": {
+ "version": "2.0.3",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "license": "MIT"
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "license": "MIT"
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/react": {
+ "version": "18.2.0",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-chartjs-2": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz",
+ "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==",
+ "peerDependencies": {
+ "chart.js": "^4.1.1",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "18.2.0",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.0"
+ },
+ "peerDependencies": {
+ "react": "^18.2.0"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "license": "MIT"
+ },
+ "node_modules/react-lifecycles-compat": {
+ "version": "3.0.4",
+ "license": "MIT"
+ },
+ "node_modules/react-smooth": {
+ "version": "2.0.5",
+ "license": "MIT",
+ "dependencies": {
+ "fast-equals": "^5.0.0",
+ "react-transition-group": "2.9.0"
+ },
+ "peerDependencies": {
+ "prop-types": "^15.6.0",
+ "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/react-transition-group": {
+ "version": "2.9.0",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "dom-helpers": "^3.4.0",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2",
+ "react-lifecycles-compat": "^3.0.4"
+ },
+ "peerDependencies": {
+ "react": ">=15.0.0",
+ "react-dom": ">=15.0.0"
+ }
+ },
+ "node_modules/recharts": {
+ "version": "2.10.3",
+ "license": "MIT",
+ "dependencies": {
+ "clsx": "^2.0.0",
+ "eventemitter3": "^4.0.1",
+ "lodash": "^4.17.19",
+ "react-is": "^16.10.2",
+ "react-smooth": "^2.0.5",
+ "recharts-scale": "^0.4.4",
+ "tiny-invariant": "^1.3.1",
+ "victory-vendor": "^36.6.8"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "prop-types": "^15.6.0",
+ "react": "^16.0.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/recharts-scale": {
+ "version": "0.4.5",
+ "license": "MIT",
+ "dependencies": {
+ "decimal.js-light": "^2.4.1"
+ }
+ },
+ "node_modules/regenerator-runtime": {
+ "version": "0.14.0",
+ "license": "MIT"
+ },
+ "node_modules/remount": {
+ "version": "1.0.0",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">= 18.0.0",
+ "react-dom": ">= 18.0.0"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.23.0",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "node_modules/tiny-invariant": {
+ "version": "1.3.1",
+ "license": "MIT"
+ },
+ "node_modules/victory-vendor": {
+ "version": "36.7.0",
+ "license": "MIT AND ISC",
+ "dependencies": {
+ "@types/d3-array": "^3.0.3",
+ "@types/d3-ease": "^3.0.0",
+ "@types/d3-interpolate": "^3.0.1",
+ "@types/d3-scale": "^4.0.2",
+ "@types/d3-shape": "^3.1.0",
+ "@types/d3-time": "^3.0.0",
+ "@types/d3-timer": "^3.0.0",
+ "d3-array": "^3.1.6",
+ "d3-ease": "^3.0.1",
+ "d3-interpolate": "^3.0.1",
+ "d3-scale": "^4.0.2",
+ "d3-shape": "^3.1.0",
+ "d3-time": "^3.0.0",
+ "d3-timer": "^3.0.1"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index 25b33f79..012c14ca 100644
--- a/package.json
+++ b/package.json
@@ -7,8 +7,13 @@
"dependencies": {
"@hotwired/stimulus": "^3.2.2",
"@hotwired/turbo-rails": "^7.3.0",
+ "bun": "^1.0.18",
+ "bundle": "^2.1.0",
+ "chart.js": "^4.4.1",
"react": "^18.2.0",
+ "react-chartjs-2": "^5.2.0",
"react-dom": "^18.2.0",
+ "recharts": "^2.10.3",
"remount": "^1.0.0"
}
-}
\ No newline at end of file
+}
diff --git a/spec/models/experiment_spec.rb b/spec/models/experiment_spec.rb
new file mode 100644
index 00000000..6910a33c
--- /dev/null
+++ b/spec/models/experiment_spec.rb
@@ -0,0 +1,7 @@
+require 'rails_helper'
+
+RSpec.describe Experiment, type: :model do
+ it 'exemplo sucesso' do
+ expect(1).to equal(1)
+ end
+end
diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb
new file mode 100644
index 00000000..823bf4d3
--- /dev/null
+++ b/spec/rails_helper.rb
@@ -0,0 +1,83 @@
+# This file is copied to spec/ when you run 'rails generate rspec:install'
+require 'spec_helper'
+
+require 'simplecov'
+require 'simplecov_json_formatter'
+
+SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
+ SimpleCov::Formatter::JSONFormatter,
+ SimpleCov::Formatter::HTMLFormatter
+])
+
+SimpleCov.start do
+ add_group 'Config', 'config'
+ add_group 'Controllers', 'app/controllers'
+ add_group 'Libs', 'lib'
+ add_group 'Models', 'app/models'
+ add_group 'Serializers', 'app/serializers'
+ add_group 'Specs', 'spec'
+end
+
+ENV['RAILS_ENV'] ||= 'test'
+require_relative '../config/environment'
+# Prevent database truncation if the environment is production
+abort("The Rails environment is running in production mode!") if Rails.env.production?
+require 'rspec/rails'
+# Add additional requires below this line. Rails is not loaded until this point!
+
+# Requires supporting ruby files with custom matchers and macros, etc, in
+# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
+# run as spec files by default. This means that files in spec/support that end
+# in _spec.rb will both be required and run as specs, causing the specs to be
+# run twice. It is recommended that you do not name files matching this glob to
+# end with _spec.rb. You can configure this pattern with the --pattern
+# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
+#
+# The following line is provided for convenience purposes. It has the downside
+# of increasing the boot-up time by auto-requiring all files in the support
+# directory. Alternatively, in the individual `*_spec.rb` files, manually
+# require only the support files necessary.
+#
+# Rails.root.glob('spec/support/**/*.rb').sort.each { |f| require f }
+
+# Checks for pending migrations and applies them before tests are run.
+# If you are not using ActiveRecord, you can remove these lines.
+begin
+ ActiveRecord::Migration.maintain_test_schema!
+rescue ActiveRecord::PendingMigrationError => e
+ abort e.to_s.strip
+end
+RSpec.configure do |config|
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
+ config.fixture_paths = [
+ Rails.root.join('spec/fixtures')
+ ]
+
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
+ # examples within a transaction, remove the following line or assign false
+ # instead of true.
+ config.use_transactional_fixtures = true
+
+ # You can uncomment this line to turn off ActiveRecord support entirely.
+ # config.use_active_record = false
+
+ # RSpec Rails can automatically mix in different behaviours to your tests
+ # based on their file location, for example enabling you to call `get` and
+ # `post` in specs under `spec/controllers`.
+ #
+ # You can disable this behaviour by removing the line below, and instead
+ # explicitly tag your specs with their type, e.g.:
+ #
+ # RSpec.describe UsersController, type: :controller do
+ # # ...
+ # end
+ #
+ # The different available types are documented in the features, such as in
+ # https://rspec.info/features/6-0/rspec-rails
+ config.infer_spec_type_from_file_location!
+
+ # Filter lines from Rails gems in backtraces.
+ config.filter_rails_from_backtrace!
+ # arbitrary gems may also be filtered via:
+ # config.filter_gems_from_backtrace("gem name")
+end
diff --git a/spec/requests/graph_controller_spec.rb b/spec/requests/graph_controller_spec.rb
new file mode 100644
index 00000000..a7623f32
--- /dev/null
+++ b/spec/requests/graph_controller_spec.rb
@@ -0,0 +1,205 @@
+require 'rails_helper.rb'
+
+RSpec.describe "GraphController", type: :request do
+ describe "GET graph#skillsGet" do
+ let(:trial_execution) { instance_double(TrialExecution, {id: 99, log: "0.00, [DEBUG], nurse, NURSE_CONFIG={'location': 'PC Room 3', 'position': [-28.5, 18.0, -1.57]}, None, None
+ 0.00, [INFO], robot2, {'battery-level': '63.50'}, None, None
+ 0.11, [DEBUG], logger, ROBOTS_CONFIG={'avg_speed': 0.15, 'battery_charge': 0.634952869577942, 'battery_discharge_rate': 0.00054, 'id': 2, 'local_plan': [['navigation', ['PC Room 3', [[-21.0, 18.0, -1.57], [-21.0, 16.0], [-28.5, 16.0], [-28.5, 18.0, -1.57]]], 'navto_room'], ['approach_person', ['nurse'], 'approach_nurse'], ['authenticate_person', ['nurse'], 'authenticate_nurse'], ['operate_drawer', ['open'], 'open_drawer_for_nurse'], ['send_message', ['nurse'], 'notify_nurse_of_open_drawer_for_nurse_completed'], ['wait_message', ['nurse'], 'wait_nurse_to_complete_deposit'], ['operate_drawer', ['close'], 'close_drawer_nurse'], ['navigation', ['Laboratory', [[-28.5, 18.0, -1.57], [-28.5, 16.0], [-26.0, 16.0], [-26.0, 13.0, 1.57]]], 'navto_lab'], ['approach_robot', ['lab_arm'], 'approach_arm'], ['operate_drawer', ['open'], 'open_drawer_lab'], ['send_message', ['lab_arm'], 'notify_lab_arm_of_open_drawer_lab_completed'], ['wait_message', ['lab_arm'], 'wait_lab_arm_to_complete_pick_up_sample'], ['operate_drawer', ['close'], 'close_drawer_lab']], 'location': 'PC Room 5', 'name': 'r2', 'position': [-21.0, 18.0, -1.57], 'skills': ['approach_person', 'approach_robot', 'authenticate_person', 'navigation', 'operate_drawer']}, None, None
+ 0.11, [DEBUG], logger, Simulation open, None, None
+ 10.07, [INFO], robot2, {'y': 17.977, 'x': -20.956, 'yaw': -3.141}, None, None
+ 10.07, [INFO], robot2, {'battery-level': '63.01'}, None, None
+ 20.01, [INFO], robot2, {'battery-level': '62.47'}, None, None
+ 20.01, [INFO], robot2, {'y': 16.197, 'x': -21.448, 'yaw': 3.141}, None, None
+ 30.05, [INFO], robot2, {'battery-level': '61.93'}, None, None
+ 30.05, [INFO], robot2, {'y': 16.101, 'x': -23.573, 'yaw': 3.141}, None, None
+ 40.01, [INFO], robot2, {'battery-level': '61.39'}, None, None
+ 40.01, [INFO], robot2, {'y': 16.087, 'x': -25.671, 'yaw': 3.141}, None, None
+ 50.01, [INFO], robot2, {'battery-level': '60.85'}, None, None
+ 50.01, [INFO], robot2, {'y': 16.095, 'x': -27.778, 'yaw': -3.139}, None, None
+ 60.06, [INFO], robot2, {'battery-level': '60.31'}, None, None
+ 60.06, [INFO], robot2, {'y': 17.701, 'x': -28.627, 'yaw': 3.142}, None, None
+ 70.02, [INFO], robot2, {'battery-level': '59.77'}, None, None
+ 70.02, [INFO], robot2, {'y': 17.975, 'x': -28.622, 'yaw': -3.142}, None, None
+ 73.92, [info], nurse, sync, received-request, (status=sending-request)
+ 73.92, [info], nurse, sync, request-sent, (status=waiting)
+ 73.92, [info], nurse, sync, wait-message, (status=message-received)
+ 80.03, [INFO], robot2, {'battery-level': '59.23'}, None, None
+ 80.03, [INFO], robot2, {'y': 16.913, 'x': -28.699, 'yaw': 3.139}, None, None
+ 89.94, [INFO], robot2, {'battery-level': '58.69'}, None, None
+ 90.03, [INFO], robot2, {'y': 16.168, 'x': -27.158, 'yaw': 3.142}, None, None
+ 100.05, [INFO], robot2, {'y': 14.855, 'x': -25.881, 'yaw': -3.142}, None, None
+ 100.05, [INFO], robot2, {'battery-level': '58.15'}, None, None
+ 110.03, [INFO], robot2, {'battery-level': '57.61'}, None, None
+ 110.03, [INFO], robot2, {'y': 13.027, 'x': -25.904, 'yaw': 3.141}, None, None
+ 118.29, [info], lab_arm, sync, wait-message, (status=message-received)
+ 119.17, [WARN], robot2, SUCCESS, None, None"}) }
+
+
+ before do
+ allow(TrialExecution).to receive(:find_by).with(id: trial_execution.id.to_s).and_return(trial_execution)
+ get "/graph/skillsGet/99"
+ end
+
+ it "Acessa graph/skillsGet" do
+ expect(response).to have_http_status(200)
+ end
+ end
+
+ describe "GET graph#skillsGet" do
+ # Se nao existe linha com local plan, nao captura skill
+ let(:trial_execution) { instance_double(TrialExecution, {id: 98, log: "[WARN], logger, TIMEOUT, None, None" })}
+
+ before do
+ allow(TrialExecution).to receive(:find_by).with(id: trial_execution.id.to_s).and_return(trial_execution)
+ get "/graph/skillsGet/98"
+ end
+
+ it 'Erro: sem iniciação do local_plan' do
+ parsed_response = JSON.parse(response.body)
+
+ expect(parsed_response).to eq([])
+ end
+ end
+
+ # Necessita-se de uma linha com local plan para se identificar as proximas skills
+ describe "GET graph#skillsGet" do
+ let(:trial_execution) { instance_double(TrialExecution, {id: 97, log: "0.20, [DEBUG], logger, ROBOTS_CONFIG={'local_plan': [['navigation', ['IC Room 6', [[-39.44, 33.95, 0.0], [-37.0, 33.95], [-37.0, 18.93], [-33.9, 18.93, 3.14]]], 'navto_room'], ['approach_person', ['nurse'], 'approach_nurse'], ['authenticate_person', ['nurse'], 'authenticate_nurse'], ['operate_drawer', ['open'], 'open_drawer_for_nurse'], ['send_message', ['nurse'], 'notify_nurse_of_open_drawer_for_nurse_completed'], ['wait_message', ['nurse'], 'wait_nurse_to_complete_deposit'], ['operate_drawer', ['close'], 'close_drawer_nurse'], ['navigation', ['Laboratory', [[-33.9, 18.93, 3.14], [-37.0, 18.93],
+ [WARN], logger, TIMEOUT, None, None" })}
+
+ before do
+ allow(TrialExecution).to receive(:find_by).with(id: trial_execution.id.to_s).and_return(trial_execution)
+ end
+
+ it 'Existe local_plan e msg de TIMEOUT' do
+ get '/graph/skillsGet/97'
+ expect(response).to have_http_status(:success)
+ parsed_response = JSON.parse(response.body)
+
+ expect(parsed_response).to eq([{ "message" => "TIMEOUT" }])
+ end
+ end
+
+ # Msg de Start
+ describe "GET graph#skillsGet" do
+ let(:trial_execution) { instance_double(TrialExecution, {id: 96, log: "0.11, [DEBUG], logger, ROBOTS_CONFIG={'local_plan': [['navigation', ['IC Room 2', [[-33.9, 18.93, 3.14], [-37.0, 18.93], [-37.0, 21.5], [-38.0, 21.5, 0.0]]], 'navto_room'], ['approach_person', ['nurse'], 'approach_nurse'], ['authenticate_person', ['nurse'], 'authenticate_nurse'], ['operate_drawer', ['open'], 'open_drawer_for_nurse'], ['send_message', ['nurse'], 'notify_nurse_of_open_drawer_for_nurse_completed'], ['wait_message', ['nurse'], 'wait_nurse_to_complete_deposit'], ['operate_drawer', ['close'], 'close_drawer_nurse'], ['navigation', ['Laboratory', [[-38.0, 21.5, 0.0], [-37.0, 21.5], [-37.0, 16.0], [-26.0, 16.0], [-26.0, 13.0, 1.57]]], 'navto_lab'], ['approach_robot', ['lab_arm'], 'approach_arm'], ['operate_drawer', ['open'], 'open_drawer_lab'], ['send_message', ['lab_arm'], 'notify_lab_arm_of_open_drawer_lab_completed'], ['wait_message', ['lab_arm'], 'wait_lab_arm_to_complete_pick_up_sample'], ['operate_drawer', ['close'], 'close_drawer_lab']], 'location': 'IC Room 6', 'name': 'r6', 'position': [-33.9, 18.93, 3.14], 'skills': ['approach_person', 'approach_robot', 'navigation', 'operate_drawer']}, None, None
+ 0.11, [DEBUG], logger, Simulation open, None, None"} )}
+
+ before do
+ allow(TrialExecution).to receive(:find_by).with(id: trial_execution.id.to_s).and_return(trial_execution)
+ end
+
+ it 'Comando de start' do
+ get '/graph/skillsGet/96'
+ expect(response).to have_http_status(:success)
+ parsed_response = JSON.parse(response.body)
+
+ expect(parsed_response).to eq([{ "message" => "Experiment started!"}])
+ end
+ end
+
+ # Msg de Navigation to room + Falta de skill
+ describe "GET graph#skillsGet" do
+ let(:trial_execution) { instance_double(TrialExecution, {id: 95, log: "0.11, [DEBUG], logger, ROBOTS_CONFIG={'local_plan': [['navigation', ['IC Room 2', [[-33.9, 18.93, 3.14], [-37.0, 18.93], [-37.0, 21.5], [-38.0, 21.5, 0.0]]], 'navto_room'], ['approach_person', ['nurse'], 'approach_nurse'], ['authenticate_person', ['nurse'], 'authenticate_nurse'], ['operate_drawer', ['open'], 'open_drawer_for_nurse'], ['send_message', ['nurse'], 'notify_nurse_of_open_drawer_for_nurse_completed'], ['wait_message', ['nurse'], 'wait_nurse_to_complete_deposit'], ['operate_drawer', ['close'], 'close_drawer_nurse'], ['navigation', ['Laboratory', [[-38.0, 21.5, 0.0], [-37.0, 21.5], [-37.0, 16.0], [-26.0, 16.0], [-26.0, 13.0, 1.57]]], 'navto_lab'], ['approach_robot', ['lab_arm'], 'approach_arm'], ['operate_drawer', ['open'], 'open_drawer_lab'], ['send_message', ['lab_arm'], 'notify_lab_arm_of_open_drawer_lab_completed'], ['wait_message', ['lab_arm'], 'wait_lab_arm_to_complete_pick_up_sample'], ['operate_drawer', ['close'], 'close_drawer_lab']], 'location': 'IC Room 6', 'name': 'r6', 'position': [-33.9, 18.93, 3.14], 'skills': ['approach_person', 'approach_robot', 'navigation', 'operate_drawer']}, None, None
+ 0.11, [DEBUG], logger, Simulation open, None, None
+ 10.03, [INFO], robot6, {'y': 18.858, 'x': -33.903, 'yaw': -3.141}, None, None
+ 10.03, [INFO], robot6, {'battery-level': '53.44'}, None, None
+ 55.48, [WARN], robot6, NO-SKILL, authenticate_person, authenticate_nurse"})}
+
+ before do
+ allow(TrialExecution).to receive(:find_by).with(id: trial_execution.id.to_s).and_return(trial_execution)
+ end
+
+ it 'Navigation to room + Falta de skill' do
+ get '/graph/skillsGet/95'
+ expect(response).to have_http_status(:success)
+ parsed_response = JSON.parse(response.body)
+
+ expect(parsed_response).to eq([{"message" => "Experiment started!"}, {"time" => 10.03,"message" => "Navigation to room"}, {"time" => 10.03,"message" => "Navigation to room"}, {"time" => "55.48","message" => "Experiment failed with NO-SKILL: authenticate_person."}])
+ end
+ end
+
+ # Msg de falha em skill
+ describe "GET graph#skillsGet" do
+ let(:trial_execution) { instance_double(TrialExecution, {id: 94, log: "0.19, [DEBUG], logger, ROBOTS_CONFIG={'local_plan': [['navigation', ['IC Room 6', [[-38.0, 21.5, 0.0], [-37.0, 21.5], [-37.0, 18.93], [-33.9, 18.93, 3.14]]], 'navto_room'], ['approach_person', ['nurse'], 'approach_nurse'], ['authenticate_person', ['nurse'], 'authenticate_nurse'], ['operate_drawer', ['open'], 'open_drawer_for_nurse'], ['send_message', ['nurse'], 'notify_nurse_of_open_drawer_for_nurse_completed'], ['wait_message', ['nurse'], 'wait_nurse_to_complete_deposit'], ['operate_drawer', ['close'], 'close_drawer_nurse'], ['navigation', ['Laboratory', [[-33.9, 18.93, 3.14], [-37.0, 18.93], [-37.0, 16.0], [-26.0, 16.0], [-26.0, 13.0, 1.57]]], 'navto_lab'], ['approach_robot', ['lab_arm'], 'approach_arm'], ['operate_drawer', ['open'], 'open_drawer_lab'], ['send_message', ['lab_arm'], 'notify_lab_arm_of_open_drawer_lab_completed'], ['wait_message', ['lab_arm'], 'wait_lab_arm_to_complete_pick_up_sample'], ['operate_drawer', ['close'], 'close_drawer_lab']], 'location': 'IC Room 2', 'name': 'r3', 'position': [-38.0, 21.5, 0.0], 'skills': ['approach_person', 'approach_robot', 'authenticate_person', 'navigation', 'operate_drawer']}, None, None
+ 0.19, [DEBUG], logger, Simulation open, None, None
+ 176.81, [WARN], robot3, SKILL-FAILURE, navigation, navto_lab"})}
+
+ before do
+ allow(TrialExecution).to receive(:find_by).with(id: trial_execution.id.to_s).and_return(trial_execution)
+ end
+
+ it 'Falha em skill' do
+ get '/graph/skillsGet/94'
+ expect(response).to have_http_status(:success)
+ parsed_response = JSON.parse(response.body)
+
+ expect(parsed_response).to eq([{"message" => "Experiment started!"}, {"time" => "176.81","message" => "Skill navigation failed."}])
+ end
+ end
+
+ # Msg de envio para enfermeira
+ describe "GET graph#skillsGet" do
+ let(:trial_execution) { instance_double(TrialExecution, {id: 93, log: "0.19, [DEBUG], logger, ROBOTS_CONFIG={'local_plan': [['navigation', ['IC Room 6', [[-38.0, 21.5, 0.0], [-37.0, 21.5], [-37.0, 18.93], [-33.9, 18.93, 3.14]]], 'navto_room'], ['approach_person', ['nurse'], 'approach_nurse'], ['authenticate_person', ['nurse'], 'authenticate_nurse'], ['operate_drawer', ['open'], 'open_drawer_for_nurse'], ['send_message', ['nurse'], 'notify_nurse_of_open_drawer_for_nurse_completed'], ['wait_message', ['nurse'], 'wait_nurse_to_complete_deposit'], ['operate_drawer', ['close'], 'close_drawer_nurse'], ['navigation', ['Laboratory', [[-33.9, 18.93, 3.14], [-37.0, 18.93], [-37.0, 16.0], [-26.0, 16.0], [-26.0, 13.0, 1.57]]], 'navto_lab'], ['approach_robot', ['lab_arm'], 'approach_arm'], ['operate_drawer', ['open'], 'open_drawer_lab'], ['send_message', ['lab_arm'], 'notify_lab_arm_of_open_drawer_lab_completed'], ['wait_message', ['lab_arm'], 'wait_lab_arm_to_complete_pick_up_sample'], ['operate_drawer', ['close'], 'close_drawer_lab']], 'location': 'IC Room 2', 'name': 'r3', 'position': [-38.0, 21.5, 0.0], 'skills': ['approach_person', 'approach_robot', 'authenticate_person', 'navigation', 'operate_drawer']}, None, None
+ 0.19, [DEBUG], logger, Simulation open, None, None
+ 68.58, [info], nurse, sync, received-request, (status=sending-request)
+ 68.58, [info], nurse, sync, request-sent, (status=waiting)
+ 68.58, [info], nurse, sync, wait-message, (status=message-received)"})}
+
+ before do
+ allow(TrialExecution).to receive(:find_by).with(id: trial_execution.id.to_s).and_return(trial_execution)
+ end
+
+ it 'Envio de mensagem - enfermeira' do
+ get '/graph/skillsGet/93'
+ expect(response).to have_http_status(:success)
+ parsed_response = JSON.parse(response.body)
+
+ expect(parsed_response).to eq([{"message" => "Experiment started!"}, {"time" => "68.58","message" => "Sending message to nurse"},{"time" => "68.58","message" => "Waiting the message get to nurse"},{"time" => "68.58","message" => "Message sent to nurse"}])
+ end
+ end
+
+ # Msg de Navigation to lab + envio para lab_arm
+ describe "GET graph#skillsGet" do
+ let(:trial_execution) { instance_double(TrialExecution, {id: 92, log: "0.19, [DEBUG], logger, ROBOTS_CONFIG={'local_plan': [['navigation', ['IC Room 6', [[-38.0, 21.5, 0.0], [-37.0, 21.5], [-37.0, 18.93], [-33.9, 18.93, 3.14]]], 'navto_room'], ['approach_person', ['nurse'], 'approach_nurse'], ['authenticate_person', ['nurse'], 'authenticate_nurse'], ['operate_drawer', ['open'], 'open_drawer_for_nurse'], ['send_message', ['nurse'], 'notify_nurse_of_open_drawer_for_nurse_completed'], ['wait_message', ['nurse'], 'wait_nurse_to_complete_deposit'], ['operate_drawer', ['close'], 'close_drawer_nurse'], ['navigation', ['Laboratory', [[-33.9, 18.93, 3.14], [-37.0, 18.93], [-37.0, 16.0], [-26.0, 16.0], [-26.0, 13.0, 1.57]]], 'navto_lab'], ['approach_robot', ['lab_arm'], 'approach_arm'], ['operate_drawer', ['open'], 'open_drawer_lab'], ['send_message', ['lab_arm'], 'notify_lab_arm_of_open_drawer_lab_completed'], ['wait_message', ['lab_arm'], 'wait_lab_arm_to_complete_pick_up_sample'], ['operate_drawer', ['close'], 'close_drawer_lab']], 'location': 'IC Room 2', 'name': 'r3', 'position': [-38.0, 21.5, 0.0], 'skills': ['approach_person', 'approach_robot', 'authenticate_person', 'navigation', 'operate_drawer']}, None, None
+ 0.19, [DEBUG], logger, Simulation open, None, None
+ 10.01, [INFO], robot4, {'battery-level': '64.70'}, None, None
+ 10.01, [INFO], robot4, {'y': 34.241, 'x': -33.519, 'yaw': 3.142}, None, None
+ 108.75, [info], nurse, sync, received-request, (status=sending-request)
+ 108.75, [info], nurse, sync, request-sent, (status=waiting)
+ 108.75, [info], nurse, sync, wait-message, (status=message-received)
+ 110.04, [INFO], robot4, {'battery-level': '59.10'}, None, None
+ 110.04, [INFO], robot4, {'y': 21.387, 'x': -38.117, 'yaw': 3.139}, None, None
+ 210.62, [info], lab_arm, sync, wait-message, (status=message-received)"})}
+
+ before do
+ allow(TrialExecution).to receive(:find_by).with(id: trial_execution.id.to_s).and_return(trial_execution)
+ end
+
+ it 'Navigation to lab + lab_arm' do
+ get '/graph/skillsGet/92'
+ expect(response).to have_http_status(:success)
+ parsed_response = JSON.parse(response.body)
+
+ expect(parsed_response).to eq([{"message" => "Experiment started!"}, {"time" => 10.01,"message" => "Navigation to room"},{"time" => 10.01,"message" => "Navigation to room"}, {"time" => "108.75","message" => "Sending message to nurse"},{"time" => "108.75","message" => "Waiting the message get to nurse"},{"time" => "108.75","message" => "Message sent to nurse"}, {"time" => 110.04,"message" => "Navigation to lab"}, {"time" => 110.04,"message" => "Navigation to lab"}, {"time" => "210.62","message" => "Message sent to lab_arm"}])
+ end
+ end
+
+ # Msg de Sucesso!
+ describe "GET graph#skillsGet" do
+ let(:trial_execution) { instance_double(TrialExecution, {id: 91, log: "0.13, [DEBUG], logger, ROBOTS_CONFIG={'local_plan': [['navigation', ['PC Room 3', [[-27.23, 18.0, -1.57], [-27.23, 16.0], [-28.5, 16.0], [-28.5, 18.0, -1.57]]], 'navto_room'], ['approach_person', ['nurse'], 'approach_nurse'], ['authenticate_person', ['nurse'], 'authenticate_nurse'], ['operate_drawer', ['open'], 'open_drawer_for_nurse'], ['send_message', ['nurse'], 'notify_nurse_of_open_drawer_for_nurse_completed'], ['wait_message', ['nurse'], 'wait_nurse_to_complete_deposit'], ['operate_drawer', ['close'], 'close_drawer_nurse'], ['navigation', ['Laboratory', [[-28.5, 18.0, -1.57], [-28.5, 16.0], [-26.0, 16.0], [-26.0, 13.0, 1.57]]], 'navto_lab'], ['approach_robot', ['lab_arm'], 'approach_arm'], ['operate_drawer', ['open'], 'open_drawer_lab'], ['send_message', ['lab_arm'], 'notify_lab_arm_of_open_drawer_lab_completed'], ['wait_message', ['lab_arm'], 'wait_lab_arm_to_complete_pick_up_sample'], ['operate_drawer', ['close'], 'close_drawer_lab']], 'location': 'PC Room 4', 'name': 'r6', 'position': [-27.23, 18.0, -1.57], 'skills': ['approach_person', 'approach_robot', 'authenticate_person', 'navigation', 'operate_drawer']}, None, None
+ 0.13, [DEBUG], logger, Simulation open, None, None
+ 81.78, [WARN], robot6, SUCCESS, None, None"})}
+
+ before do
+ allow(TrialExecution).to receive(:find_by).with(id: trial_execution.id.to_s).and_return(trial_execution)
+ end
+
+ it 'Sucesso!' do
+ get '/graph/skillsGet/91'
+ expect(response).to have_http_status(:success)
+ parsed_response = JSON.parse(response.body)
+
+ expect(parsed_response).to eq([{"message" => "Experiment started!"}, {"time" => "81.78","message" => "Experiment completed successfully with 81.78 seconds!"}])
+ end
+ end
+end
diff --git a/spec/requests/velocity_controller_spec.rb b/spec/requests/velocity_controller_spec.rb
new file mode 100644
index 00000000..4470cf2b
--- /dev/null
+++ b/spec/requests/velocity_controller_spec.rb
@@ -0,0 +1,70 @@
+require 'rails_helper.rb'
+
+RSpec.describe "VelocityController", type: :request do
+ describe "GET velocity#getData" do
+ it "Acessa velocity/getData/:id" do
+ get '/velocity/getData/1'
+ expect(response).to have_http_status(200)
+ end
+ end
+
+
+ describe "GET velocity#getData" do
+ Experiment.create([ name: "teste", disabled: false ])
+ Trial.create([name: "testeTrial", disabled: false, deleted: false, runs: 20, experiment_id: Experiment.first.id])
+ TrialExecution.create(status: "bom", log:"10.02, [INFO], robot4, {'battery-level': '17.26'}, None, None;
+ 10.02, [INFO], robot4, {'y': 34.57, 'x': -38.121, 'yaw': -3.142}, None, None;
+ 19.99, [INFO], robot4, {'battery-level': '17.00'}, None, None;
+ 19.99, [INFO], robot4, {'y': 33.238, 'x': -37.019, 'yaw': 3.141}, None, None;
+ 30.01, [INFO], robot4, {'battery-level': '16.74'}, None, None;
+ 30.01, [INFO], robot4, {'y': 31.112, 'x': -37.069, 'yaw': 3.141}, None, None;
+ 39.97, [INFO], robot4, {'y': 28.987, 'x': -37.012, 'yaw': 3.14}, None, None;
+ 39.97, [INFO], robot4, {'battery-level': '16.48'}, None, None;
+ 49.98, [INFO], robot4, {'battery-level': '16.22'}, None, None;
+ 50.01, [INFO], robot4, {'y': 26.87, 'x': -37.012, 'yaw': 3.142}, None, None;
+ 60.02, [INFO], robot4, {'battery-level': '15.96'}, None, None;
+ 60.02, [INFO], robot4, {'y': 24.742, 'x': -37.047, 'yaw': 3.142}, None, None;
+ 69.98, [INFO], robot4, {'y': 22.63, 'x': -37.066, 'yaw': 3.141}, None, None;
+ 70.00, [INFO], robot4, {'battery-level': '15.70'}, None, None;
+ 79.99, [INFO], robot4, {'battery-level': '15.44'}, None, None;
+ 79.99, [INFO], robot4, {'y': 20.504, 'x': -37.147, 'yaw': 3.142}, None, None;
+ 90.02, [INFO], robot4, {'y': 18.53, 'x': -36.598, 'yaw': 3.141}, None, None;
+ 90.02, [INFO], robot4, {'battery-level': '15.18'}, None, None;
+ 100.01, [INFO], robot4, {'battery-level': '14.92'}, None, None;
+ 100.01, [INFO], robot4, {'y': 17.763, 'x': -34.786, 'yaw': -3.142}, None, None;
+ 109.99, [INFO], robot4, {'y': 18.934, 'x': -33.913, 'yaw': 3.142}, None, None;
+ 109.99, [INFO], robot4, {'battery-level': '14.66'}, None, None;
+ 113.74, [info], nurse, sync, received-request, (status=sending-request);
+ 113.74, [info], nurse, sync, request-sent, (status=waiting);
+ 113.75, [info], nurse, sync, wait-message, (status=message-received);
+ 120.00, [INFO], robot4, {'y': 18.079, 'x': -34.685, 'yaw': -3.142}, None, None;
+ 120.00, [INFO], robot4, {'battery-level': '14.40'}, None, None;
+ 129.99, [INFO], robot4, {'battery-level': '14.14'}, None, None;
+ 130.00, [INFO], robot4, {'y': 18.038, 'x': -36.106, 'yaw': -3.142}, None, None;
+ 140.02, [INFO], robot4, {'battery-level': '13.88'}, None, None;
+ 140.02, [INFO], robot4, {'y': 18.081, 'x': -36.168, 'yaw': 3.142}, None, None;
+ 149.98, [INFO], robot4, {'y': 17.575, 'x': -37.036, 'yaw': 3.142}, None, None;
+ 149.98, [INFO], robot4, {'battery-level': '13.62'}, None, None;
+ 160.01, [INFO], robot4, {'y': 17.345, 'x': -36.954, 'yaw': -3.141}, None, None;
+ 160.01, [INFO], robot4, {'battery-level': '13.36'}, None, None;
+ 170.01, [INFO], robot4, {'y': 17.274, 'x': -37.095, 'yaw': -3.141}, None, None;
+ 170.01, [INFO], robot4, {'battery-level': '13.10'}, None, None;
+ 179.97, [INFO], robot4, {'battery-level': '12.84'}, None, None;
+ 180.00, [INFO], robot4, {'y': 17.402, 'x': -37.034, 'yaw': -3.142}, None, None;
+ 190.01, [INFO], robot4, {'y': 17.205, 'x': -36.812, 'yaw': -3.141}, None, None;
+ 190.01, [INFO], robot4, {'battery-level': '12.58'}, None, None;
+ 200.02, [INFO], robot4, {'battery-level': '12.32'}, None, None;
+ 200.02, [INFO], robot4, {'y': 17.363, 'x': -36.885, 'yaw': 3.142}, None, None;
+ 210.01, [INFO], robot4, {'y': 17.131, 'x': -37.113, 'yaw': 3.142}, None, None;
+ 210.01, [INFO], robot4, {'battery-level': '12.06'}, None, None", trial_id: Trial.find_by(id: "1").id)
+
+
+ it 'Resposta correta' do
+ get '/velocity/getData/1'
+ expect(response).to have_http_status(:success)
+ parsed_response = JSON.parse(response.body)
+
+ expect(parsed_response).to eq("19.99" => 0.17339667773468448,"30.01" => 0.21223431909464818,"39.97" => 0.21343015392853468,"50.01" => 0.21085657370517905,"60.02" => 0.21261616478365847,"69.98" => 0.21205677332189088,"79.99" => 0.21254170619499257,"90.02" => 0.2042792557843504,"100.01" => 0.19696161799713408,"109.99" => 0.1463533104811412,"120.0" => 0.11508079586645197,"130.0" => 0.14215913618195625,"140.02" => 0.007530136874106592,"149.98" => 0.10087538816778391,"160.01" => 0.024344991154787752,"170.01" => 0.015786703265723092,"180.0" => 0.014193403536685849,"190.01" => 0.029650814141785335,"200.02" => 0.017387496875047776,"210.01" => 0.032560702364623904)
+ end
+ end
+end
\ No newline at end of file
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
new file mode 100644
index 00000000..327b58ea
--- /dev/null
+++ b/spec/spec_helper.rb
@@ -0,0 +1,94 @@
+# This file was generated by the `rails generate rspec:install` command. Conventionally, all
+# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
+# The generated `.rspec` file contains `--require spec_helper` which will cause
+# this file to always be loaded, without a need to explicitly require it in any
+# files.
+#
+# Given that it is always loaded, you are encouraged to keep this file as
+# light-weight as possible. Requiring heavyweight dependencies from this file
+# will add to the boot time of your test suite on EVERY test run, even for an
+# individual file that may not need all of that loaded. Instead, consider making
+# a separate helper file that requires the additional dependencies and performs
+# the additional setup, and require it from the spec files that actually need
+# it.
+#
+# See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
+RSpec.configure do |config|
+ # rspec-expectations config goes here. You can use an alternate
+ # assertion/expectation library such as wrong or the stdlib/minitest
+ # assertions if you prefer.
+ config.expect_with :rspec do |expectations|
+ # This option will default to `true` in RSpec 4. It makes the `description`
+ # and `failure_message` of custom matchers include text for helper methods
+ # defined using `chain`, e.g.:
+ # be_bigger_than(2).and_smaller_than(4).description
+ # # => "be bigger than 2 and smaller than 4"
+ # ...rather than:
+ # # => "be bigger than 2"
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
+ end
+
+ # rspec-mocks config goes here. You can use an alternate test double
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
+ config.mock_with :rspec do |mocks|
+ # Prevents you from mocking or stubbing a method that does not exist on
+ # a real object. This is generally recommended, and will default to
+ # `true` in RSpec 4.
+ mocks.verify_partial_doubles = true
+ end
+
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
+ # have no way to turn it off -- the option exists only for backwards
+ # compatibility in RSpec 3). It causes shared context metadata to be
+ # inherited by the metadata hash of host groups and examples, rather than
+ # triggering implicit auto-inclusion in groups with matching metadata.
+ config.shared_context_metadata_behavior = :apply_to_host_groups
+
+# The settings below are suggested to provide a good initial experience
+# with RSpec, but feel free to customize to your heart's content.
+=begin
+ # This allows you to limit a spec run to individual examples or groups
+ # you care about by tagging them with `:focus` metadata. When nothing
+ # is tagged with `:focus`, all examples get run. RSpec also provides
+ # aliases for `it`, `describe`, and `context` that include `:focus`
+ # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
+ config.filter_run_when_matching :focus
+
+ # Allows RSpec to persist some state between runs in order to support
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
+ # you configure your source control system to ignore this file.
+ config.example_status_persistence_file_path = "spec/examples.txt"
+
+ # Limits the available syntax to the non-monkey patched syntax that is
+ # recommended. For more details, see:
+ # https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/
+ config.disable_monkey_patching!
+
+ # Many RSpec users commonly either run the entire suite or an individual
+ # file, and it's useful to allow more verbose output when running an
+ # individual spec file.
+ if config.files_to_run.one?
+ # Use the documentation formatter for detailed output,
+ # unless a formatter has already been configured
+ # (e.g. via a command-line flag).
+ config.default_formatter = "doc"
+ end
+
+ # Print the 10 slowest examples and example groups at the
+ # end of the spec run, to help surface which specs are running
+ # particularly slow.
+ config.profile_examples = 10
+
+ # Run specs in random order to surface order dependencies. If you find an
+ # order dependency and want to debug it, you can fix the order by providing
+ # the seed, which is printed after each run.
+ # --seed 1234
+ config.order = :random
+
+ # Seed global randomization in this process using the `--seed` CLI option.
+ # Setting this allows you to use `--seed` to deterministically reproduce
+ # test failures related to randomization by passing the same `--seed` value
+ # as the one that triggered the failure.
+ Kernel.srand config.seed
+=end
+end
diff --git a/test/integration/visualizar_bateria_13.feature b/test/integration/visualizar_bateria_13.feature
deleted file mode 100644
index b4ef2d43..00000000
--- a/test/integration/visualizar_bateria_13.feature
+++ /dev/null
@@ -1,24 +0,0 @@
-Funcionalidade: Visualizar os dados de bateria do robô
-Eu como Engenheiro de Sistemas Robóticos
-Quero visualizar o status da bateria do robô em um dado momento
-A fim de monitorar e otimizar o consumo de energia durante o funcionamento contínuo do robô
-
-Contexto:
- Dado que eu esteja na interface que disponibiliza os dados de bateria
- E o sistema de teste de bateria está ativo
-
-Cenário: Consulta de dados de bateria após o término do teste (triste)
- Dado que eu solicite o status da bateria em um determinado instante
- E que esse instante não existe no teste
- Então deverá ser exibida uma mensagem informando que foi possível concluir a visualização do teste de bateria, porque o instante solicitado está fora do intervalo do teste
-
-Cenário: Status de bateria retorna dados incorretos (triste)
- Dado que eu solicite o status da bateria em um determinado instante
- E que o nível de bateria recebido pela aplicação não é um valor percentual decimal entre 0% e 100%
- Então deverá ser exibida uma mensagem informando que não foi possível concluir a visualização do teste de bateria, porque o formato dos dados é inválido
-
-Cenário: A pesquisa de status da bateria retorna porcentagem (feliz)
- Dado que eu solicite o status da bateria em um determinado instante
- E que esse instante existe no teste
- E que o nível de bateria recebido pela aplicação é um valor percentual decimal entre 0% e 100%
- Então eu deveria ver a porcentagem da bateria correspondente desse instante
\ No newline at end of file
diff --git a/test/integration/visualizar_posicao_14.feature b/test/integration/visualizar_posicao_14.feature
deleted file mode 100644
index 2d3452c6..00000000
--- a/test/integration/visualizar_posicao_14.feature
+++ /dev/null
@@ -1,24 +0,0 @@
-Funcionalidade: visualizar os dados de posição do robô
-Eu como avaliador da navegação do robo
-Quero visualizar a posição do robô em um dado momento,
-Afim de analisar os percursos escolhidos durante o teste
-
-Contexto:
- Dado que eu esteja na interfarce que disponibiliza os dados de posição
-
- Cenario: Um avaliador seleciona momento fora do escopo do teste (triste)
- Dado que o teste durou 5 minutos
- E que eu solicito a posição no momento 6:00
- E que eu clique em "gerar posição"
- Então eu deveria ver uma mensagem na tela dizendo "Não existem dados de posição para esse momento"
-
- Cenario: A pesquisa de posição retorna coordenadas (feliz)
- Dado que eu solicitei a posição no momento 6:00
- E que esse momento existe no teste
- Então eu deveria ver um conjunto de coodenadas em relação a posição do robo
-
- Cenario: A pesquisa de posição retorna dados incorretos (triste)
- Dado que eu solicitei a posição no momento 6:00
- E que esse momento existe no teste
- E que os dados recebidos pela aplicação não são númericos
- Então eu deveria ver uma mensagem na tela dizendo "não foi possível concluir a busca pelos dados"
\ No newline at end of file
diff --git a/wiki.md b/wiki.md
new file mode 100644
index 00000000..ba978999
--- /dev/null
+++ b/wiki.md
@@ -0,0 +1,34 @@
+# Projeto ide-experimentador
+
+## Grupo 6
+
+- Bernardo Ramalho Rabello (211055218)
+- Debora Venturelli Machado (190086238)
+- Emanuel de Oliveira Barbosa (211010403)
+- Pedro Arthur de Moura Neves (211055352)
+
+
+## Projeto
+
+- Scrum Master: Pedro Arthur
+- Product Owner: Emanuel de Oliveira
+
+- Issue 15: Dado que sou um usuário, quero visualizar dados de velocidade do teste executado.
+- Issue 16: Dado que sou um usuário, quero visualizar dados de skills do teste executado.
+
+
+## Funcionalidades
+### Visualizar dados de skills do teste executado
+ 1. Mostrar análise de resultados sobre as skills nos testes executados: sucesso e falha;
+ 2. Caso sucesso no item 1, apresentar funcionalidades das skills utilizadas e seus respectivos tempos;
+ 3. Caso falha no item 1, indicar o erro e interromper execução.
+ - Responsáveis: Emanuel de Oliveira e Pedro Arthur
+
+### Visualizar dados de velocidade do teste executado
+ 1. Mostrar velocidade média dos robôs nos testes executados por meio de gráficos;
+ 2. Indicar erro caso falte informação sobre a velocidade;
+ - Responsáveis: Bernardo Ramalho e Debora Venturelli
+
+## Política de Branching
+ - Branch principal
+ - Branch de desenvolvimento