Skip to content

Commit

Permalink
Start/Stop background tasks via CTS Status tab
Browse files Browse the repository at this point in the history
  • Loading branch information
jmthomas committed Feb 16, 2017
1 parent caec3d1 commit d2d4b0f
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 95 deletions.
1 change: 1 addition & 0 deletions demo/config/tools/cmd_tlm_server/cmd_tlm_server.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,6 @@ ROUTER INST_ROUTER tcpip_server_interface.rb 2055 2055 10.0 nil LENGTH 32 16 7
# LOG_RAW

BACKGROUND_TASK example_background_task.rb
STOPPED

#COLLECT_METADATA META DATA
18 changes: 6 additions & 12 deletions demo/lib/example_background_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,20 @@
require 'cosmos/tools/cmd_tlm_server/background_task'

module Cosmos

# ExampleBackgroundTask class
#
# This class is an example background task
#
# Starts by sleeping 5 seconds then sends up to three collect commands
class ExampleBackgroundTask < BackgroundTask

def initialize
super()
@name = 'Example Background Task'
@sleeper = Sleeper.new
end

def call
sent_count = 0
@sleeper = Sleeper.new
@status = "Sleeping for 5 seconds"
return if @sleeper.sleep(5) # allow interfaces time to start
loop do
#Make sure we start up with 3 collects
# Start up with at least 3 collects
if (tlm('INST', 'HEALTH_STATUS', 'COLLECTS') < 3)
begin
cmd('INST', 'COLLECT', 'TYPE' => 'NORMAL', 'DURATION' => 1)
Expand All @@ -50,8 +45,7 @@ def call

def stop
@sleeper.cancel
@status = "Stopped at #{Time.now.formatted}"
end

end # class ExampleBackgroundTask

end # module Cosmos
end
end
18 changes: 13 additions & 5 deletions lib/cosmos/tools/cmd_tlm_server/background_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,25 @@ module Cosmos
class BackgroundTask
include Api

# @return [Integer] The number of background tasks created
@@count = 0

# @return [String] Name of the background task
attr_accessor :name
# @return [Thread] Ruby thread running the task
attr_accessor :thread
# @return [String] Status message to display in the CTS
attr_accessor :status
# @return [Boolean] Whether the task is initially stopped when the CTS starts
attr_accessor :stopped

# Constructor
def initialize
@name = nil
@@count += 1
@name = "Background Task #{@@count}"
@thread = nil
@status = nil
@status = ''
@stopped = false
end

# Subclasses should override the call method which is called once by
Expand All @@ -40,7 +50,5 @@ def call
def stop
# Nothing to do by default
end

end # class BackgroundTask

end
end
64 changes: 37 additions & 27 deletions lib/cosmos/tools/cmd_tlm_server/background_tasks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
require 'cosmos/tools/cmd_tlm_server/cmd_tlm_server_config'

module Cosmos

# Manages starting and stopping all the background tasks which
# were discovered when parsing the configuration file.
class BackgroundTasks
Expand All @@ -22,37 +21,50 @@ def initialize(cmd_tlm_server_config)
@threads = []
end

# Start background tasks by creating a new Ruby thread for each and then
# calling their 'call' method once.
def start
@config.background_tasks.each do |background_task|
new_thread = Thread.new do
background_task.thread = Thread.current
begin
background_task.call
rescue Exception => err
Logger.error "Background Task thread unexpectedly died"
Cosmos.handle_fatal_exception(err)
end
end
@threads << new_thread
# Start all background tasks by creating a new Ruby thread for each and then
# calling their 'call' method once. Tasks which have stopped set to true
# are not started and must be started by calling #start.
def start_all
(0...all.length).each do |index|
start(index) unless @config.background_tasks[index].stopped
end
end

# Stop background tasks by calling their stop method and then killing their
# Ruby threads.
def stop
@config.background_tasks.each do |background_task|
# Start an individual background task by creating a new Ruby thread and then
# calling the 'call' method once.
# @param index [Integer] Which background task to start
def start(index)
@threads[index] = Thread.new do
@config.background_tasks[index].thread = Thread.current
begin
background_task.stop
rescue
# Ignore any errors because we're about to kill the thread anyway
@config.background_tasks[index].call
rescue Exception => err
Logger.error "Background Task '#{@config.background_tasks[index].name}' unexpectedly died"
Cosmos.handle_fatal_exception(err)
end
end
@threads.each {|thread| Cosmos.kill_thread(self, thread)}
end

# Stop all background tasks by calling their stop method and then killing
# their Ruby thread.
def stop_all
(0...all.length).each { |index| stop(index) }
@threads = []
end

# Stop background task by calling their stop method and then killing their
# Ruby thread.
# @param index [Integer] Which background task to stop
def stop(index)
begin
@config.background_tasks[index].stop
rescue
# Ignore any errors because we're about to kill the thread anyway
end
Cosmos.kill_thread(self, @threads[index])
@threads[index] = nil
end

# Return the array of background tasks
def all
@config.background_tasks
Expand All @@ -61,7 +73,5 @@ def all
def graceful_kill
# This method is just here to remove warnings - background_task.stop should kill the thread
end

end # class BackgroundTasks

end # module Cosmos
end
end
4 changes: 2 additions & 2 deletions lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ def start(production = false)
System.telemetry.limits_change_callback = method(:limits_change_callback)
@interfaces.start
@routers.start
@background_tasks.start
@background_tasks.start_all

# Start staleness monitor thread
@sleeper = Sleeper.new
Expand Down Expand Up @@ -221,7 +221,7 @@ def stop
# Shutdown staleness monitor thread
Cosmos.kill_thread(self, @staleness_monitor_thread)

@background_tasks.stop
@background_tasks.stop_all
@routers.stop
@interfaces.stop
@packet_logging.shutdown
Expand Down
12 changes: 7 additions & 5 deletions lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
require 'cosmos/io/raw_logger_pair'

module Cosmos

# Reads an ascii file that defines the configuration settings used to
# configure the Command/Telemetry Server.
class CmdTlmServerConfig
Expand Down Expand Up @@ -245,6 +244,11 @@ def process_file(filename, recursive = false)
@background_tasks << background_task.new
end

when 'STOPPED'
parser.verify_num_parameters(0, 0, "#{keyword}")
raise parser.error("No BACKGROUND_TASK defined") if @background_tasks.empty?
@background_tasks[-1].stopped = true

# TODO: Deprecate COLLECT_META_DATA
when 'COLLECT_METADATA', 'COLLECT_META_DATA'
parser.verify_num_parameters(2, 2, "#{keyword} <Metadata Target Name> <Metadata Packet Name>")
Expand All @@ -259,7 +263,5 @@ def process_file(filename, recursive = false)
end # loop
end
end

end # class CmdTlmServerConfig

end # module Cosmos
end
end
31 changes: 21 additions & 10 deletions lib/cosmos/tools/cmd_tlm_server/gui/status_tab.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,9 @@
require 'cosmos/gui/qt'

module Cosmos

# Implements the status tab in the Command and Telemetry Server GUI
class StatusTab

# Create the status tab and add it to the tab_widget
#
# @param tab_widget [Qt::TabWidget] The tab widget to add the tab to
def populate(tab_widget)
scroll = Qt::ScrollArea.new
Expand Down Expand Up @@ -141,22 +138,22 @@ def populate_background_status(layout)
@background_tasks_table = Qt::TableWidget.new()
@background_tasks_table.verticalHeader.hide()
@background_tasks_table.setRowCount(CmdTlmServer.background_tasks.all.length)
@background_tasks_table.setColumnCount(3)
@background_tasks_table.setHorizontalHeaderLabels(["Name", "State", "Status"])
@background_tasks_table.setColumnCount(4)
@background_tasks_table.setHorizontalHeaderLabels(["Name", "State", "Status", "Control"])

background_tasks = CmdTlmServer.background_tasks.all
if background_tasks.length > 0
row = 0
background_tasks.each_with_index do |background_task, index|
background_task_name = background_task.name
background_task_name = "Background Task ##{index + 1}" unless background_task_name
background_task_name_widget = Qt::TableWidgetItem.new(background_task_name)
background_task_name_widget = Qt::TableWidgetItem.new(background_task.name)
background_task_name_widget.setTextAlignment(Qt::AlignCenter)
@background_tasks_table.setItem(row, 0, background_task_name_widget)
button_text = 'START'
if background_task.thread
status = background_task.thread.status
status = 'complete' if status == false
background_task_state_widget = Qt::TableWidgetItem.new(status.to_s)
button_text = background_task.thread.alive? ? 'STOP' : 'START'
else
background_task_state_widget = Qt::TableWidgetItem.new('no thread')
end
Expand All @@ -165,9 +162,12 @@ def populate_background_status(layout)
@background_tasks_table.setItem(row, 1, background_task_state_widget)
background_task_status_widget = Qt::TableWidgetItem.new(background_task.status.to_s)
background_task_status_widget.setTextAlignment(Qt::AlignCenter)
background_task_status_widget.setSizeHint(Qt::Size.new(500, 30))
background_task_status_widget.setSizeHint(Qt::Size.new(400, 30))
@background_tasks_table.setItem(row, 2, background_task_status_widget)

background_task_button = Qt::PushButton.new(button_text)
background_task_button.connect(SIGNAL('clicked()')) { start_stop_task(background_task_button) }
@background_tasks_table.setCellWidget(row, 3, background_task_button)
row += 1
end
end
Expand Down Expand Up @@ -231,6 +231,8 @@ def update_background_task_status
status = background_task.thread.status
status = 'complete' if status == false
@background_tasks_table.item(row, 1).setText(status.to_s)
text = background_task.thread.alive? ? 'STOP' : 'START'
@background_tasks_table.cellWidget(row, 3).setText(text)
else
@background_tasks_table.item(row, 1).setText('no thread')
end
Expand All @@ -240,5 +242,14 @@ def update_background_task_status
end
end

# Start or stop the background task
def start_stop_task(button)
row = @background_tasks_table.indexAt(button.pos()).row
if button.text == 'STOP'
CmdTlmServer.background_tasks.stop(row)
else
CmdTlmServer.background_tasks.start(row)
end
end
end
end # module Cosmos
end
18 changes: 15 additions & 3 deletions spec/tools/cmd_tlm_server/background_task_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,25 @@
require 'cosmos/tools/cmd_tlm_server/background_task'

module Cosmos

describe BackgroundTask do
describe "initialize" do
it "initializes local variables" do
b1 = BackgroundTask.new
expect(b1.name).to eq "Background Task 1"
expect(b1.thread).to be_nil
expect(b1.status).to eq ''
expect(b1.stopped).to eq false
b2 = BackgroundTask.new
expect(b2.name).to eq "Background Task 2"
expect(b1.thread).to be_nil
expect(b1.status).to eq ''
expect(b2.stopped).to eq false
end
end

describe "call" do
it "raises an error" do
expect { BackgroundTask.new.call }.to raise_error
expect { BackgroundTask.new.call }.to raise_error(/must be defined by subclass/)
end
end

Expand All @@ -29,4 +42,3 @@ module Cosmos
end
end
end

0 comments on commit d2d4b0f

Please sign in to comment.