TACNET is now able to dynamically sync configs on initial connection, added error sound, made tacnet status dialog update stats, made simulator clock stop after all robots have run out of 'states' to run, changed some dialogs titlebar and borders to be different colors, misc. other changes.

This commit is contained in:
2020-09-08 19:25:04 -05:00
parent 08ada79e5b
commit 9dc3caca0f
17 changed files with 257 additions and 41 deletions

View File

@@ -14,6 +14,12 @@ module TAC
@config.configuration.updated_at = Time.now
@config.configuration.revision += 1
@config_changed = true
save_config
if @tacnet.connected?
upload_config(@config.name)
end
end
def config_changed?
@@ -26,8 +32,9 @@ module TAC
end
end
def save_config(name)
json = @config.to_json
def save_config(name = nil, json = nil)
name = @config.name unless name
json = @config.to_json unless name && json
File.open("#{TAC::CONFIGS_PATH}/#{name}.json", "w") { |f| f.write json }
@@ -35,15 +42,15 @@ module TAC
end
def upload_config(config_name)
if @config && @tacnet.connected?
json = @config.to_json
@tacnet.puts(TAC::TACNET::PacketHandler.packet_upload_config(config_name, json))
if @tacnet.connected?
json = Config.new(config_name).to_json
@tacnet.puts( TAC::TACNET::PacketHandler.packet_upload_config(config_name, json) )
end
end
def download_config(config_name)
if @config && @tacnet.connected?
@tacnet.puts(TAC::TACNET::PacketHandler.packet_download_config(config_name))
if @tacnet.connected?
@tacnet.puts( TAC::TACNET::PacketHandler.packet_download_config(config_name) )
end
end

View File

@@ -1,7 +1,8 @@
module TAC
class Config
attr_reader :configuration, :groups, :presets
attr_reader :name, :configuration, :groups, :presets
def initialize(name)
@name = name
@configuration = nil
@groups = nil
@presets = nil
@@ -109,23 +110,24 @@ module TAC
end
class Action
attr_accessor :name, :enabled
attr_accessor :name, :comment, :enabled
attr_reader :variables
def initialize(name:, enabled:, variables:)
@name, @enabled = name, enabled
def initialize(name:, comment:, enabled:, variables:)
@name, @comment, @enabled = name, comment, enabled
@variables = variables
end
def to_json(*args)
{
name: @name,
comment: @comment,
enabled: @enabled,
variables: @variables
}.to_json(*args)
end
def self.from_json(hash)
Action.new(name: hash[:name], enabled: hash[:enabled], variables: hash[:variables].map { |h| Variable.from_json(h) })
Action.new(name: hash[:name], comment: hash[:comment], enabled: hash[:enabled], variables: hash[:variables].map { |h| Variable.from_json(h) })
end
end

View File

@@ -10,12 +10,12 @@ module TAC
@dialog_root = stack width: 250, height: 400, border_thickness: 2, border_color: [TAC::Palette::TIMECRAFTERS_PRIMARY, TAC::Palette::TIMECRAFTERS_SECONDARY] do
# Title bar
flow width: 1.0, height: 0.1 do
@titlebar = flow width: 1.0, height: 0.1 do
background [TAC::Palette::TIMECRAFTERS_PRIMARY, TAC::Palette::TIMECRAFTERS_SECONDARY]
# title
flow width: 0.855 do
label @title, text_size: THEME_SUBHEADING_TEXT_SIZE
label @title, text_size: THEME_SUBHEADING_TEXT_SIZE, text_shadow_color: Gosu::Color::BLACK
end
# Buttons
@@ -27,11 +27,14 @@ module TAC
end
# Dialog body
stack width: 1.0, height: 0.9 do
build
@dialog_content = stack width: 1.0, height: 0.9 do
end
end
@dialog_content.clear do
build
end
center_dialog
end

View File

@@ -0,0 +1,52 @@
module TAC
class Dialog
class ActionDialog < Dialog
def build
background Gosu::Color::GRAY
@type = @options[:action].type if @options[:action]
label "Name"
@name_error = label "Error", color: TAC::Palette::TACNET_CONNECTION_ERROR
@name_error.hide
@name = edit_line @options[:action] ? @options[:action].name : "", width: 1.0
label "Comment"
@comment = edit_line @options[:action] ? @options[:action].comment : "", width: 1.0
flow width: 1.0 do
button "Cancel", width: 0.475 do
close
end
button @options[:action] ? "Update" : "Add", width: 0.475 do |b|
if valid?
if @options[:action]
@options[:callback_method].call(@options[:action], @name.value.strip, @comment.value.strip)
else
@options[:callback_method].call(@name.value.strip, @comment.value.strip)
end
close
end
end
end
end
def valid?
valid = true
if @name.value.strip.empty?
@name_error.value = "Error: Name cannot be blank\n or only whitespace."
@name_error.show
valid = false
else
@name_error.value = ""
@name_error.hide
end
return valid
end
end
end
end

View File

@@ -2,6 +2,9 @@ module TAC
class Dialog
class ConfirmDialog < Dialog
def build
@dialog_root.style.border_color = [ Palette::ALERT, darken(Palette::ALERT, 50) ]
@titlebar.style.background = [ Palette::ALERT, darken(Palette::ALERT, 50) ]
background Gosu::Color::GRAY
label @options[:message]
@@ -9,7 +12,7 @@ module TAC
button "Cancel", width: 0.475 do
close
end
button "Okay", width: 0.475 do
button "Okay", width: 0.475, **TAC::THEME_DANGER_BUTTON do
close
@options[:callback_method].call

View File

@@ -0,0 +1,25 @@
module TAC
class Dialog
class TACNETDialog < Dialog
def build
@dialog_root.style.border_color = [ Palette::TACNET_PRIMARY, Palette::TACNET_SECONDARY ]
@titlebar.style.background = [ Palette::TACNET_PRIMARY, Palette::TACNET_SECONDARY ]
background Gosu::Color::GRAY
label @options[:message]
@sound = Gosu::Sample.new(TAC::ROOT_PATH + "/media/error_alarm.ogg").play(1, 1, true)
button "Close", width: 1.0 do
close
end
end
def close
super
@sound.stop
end
end
end
end

View File

@@ -0,0 +1,24 @@
module TAC
class Dialog
class TACNETStatusDialog < Dialog
def build
background Gosu::Color::GRAY
@message_label = label $window.backend.tacnet.full_status
button "Close", width: 1.0 do
close
end
@timer = CyberarmEngine::Timer.new(1000.0) do
@message_label.value = $window.backend.tacnet.full_status
end
end
def update
super
@timer.update
end
end
end
end

View File

@@ -12,8 +12,8 @@ module TAC
BLUE_ALLIANCE = Gosu::Color.new(0xff_000080)
RED_ALLIANCE = Gosu::Color.new(0xff_800000)
TACNET_PRIMARY = Gosu::Color.new(0xff_003f7f)
TACNET_SECONDARY = Gosu::Color.new(0xff_007f7f)
TACNET_PRIMARY = Gosu::Color.new(0xff000080)
TACNET_SECONDARY = Gosu::Color.new(0xff000060)
GROUPS_PRIMARY = Gosu::Color.new(0xff_444444)
GROUPS_SECONDARY = Gosu::Color.new(0xff_444444)
@@ -26,5 +26,7 @@ module TAC
EDITOR_PRIMARY = Gosu::Color.new(0xff_446688)
EDITOR_SECONDARY = Gosu::Color.new(0xff_224466)
ALERT = TACNET_CONNECTING
end
end

View File

@@ -9,7 +9,7 @@ module TAC
@position = CyberarmEngine::Vector.new
@scale = 1
@size = 0
@field_size = 144 # inches [1 pxel = 1 inch]
@field_size = 144 # inches [1 pixel = 1 inch]
@blue = Gosu::Color.new(0xff_004080)
@red = Gosu::Color.new(0xff_800000)

View File

@@ -36,7 +36,7 @@ module TAC
push_state(ManageConfigurations)
end
button get_image("#{TAC::ROOT_PATH}/media/icons/save.png"), image_width: THEME_ICON_SIZE, margin_left: 10, tip: "Save config and settings to disk" do
window.backend.save_config(window.backend.settings.config)
window.backend.save_config
window.backend.save_settings
end
button get_image("#{TAC::ROOT_PATH}/media/icons/export.png"), image_width: THEME_ICON_SIZE, margin_left: 10, tip: "Upload local config to remote, if connected." do
@@ -81,7 +81,7 @@ module TAC
end
end
button get_image("#{TAC::ROOT_PATH}/media/icons/information.png"), image_width: THEME_ICON_SIZE, width: 0.475 do
push_state(Dialog::AlertDialog, title: "TACNET Status", message: window.backend.tacnet.full_status)
push_state(Dialog::TACNETStatusDialog, title: "TACNET Status", message: window.backend.tacnet.full_status)
end
end
end
@@ -193,6 +193,10 @@ module TAC
@tacnet_status.value = "Connection Error"
@tacnet_status.background = TAC::Palette::TACNET_CONNECTION_ERROR
if @tacnet_connection_button.value != "Connect"
push_state(Dialog::TACNETDialog, title: "TACNET Error", message: window.backend.tacnet.full_status)
end
@tacnet_connection_button.value = "Connect"
when :not_connected
@tacnet_status.value = "Not Connected"

View File

@@ -73,8 +73,13 @@ robot.forward 100"
def update
super
@simulation.update if @simulation
@simulation_status.value = "Time: #{((Gosu.milliseconds - @simulation_start_time) / 1000.0).round(1)} seconds" if @simulation_start_time
if @simulation
@simulation.update
unless @simulation.robots.all? { |robot| robot.queue.empty? } # Only update clock if simulation is running
@simulation_status.value = "Time: #{((Gosu.milliseconds - @simulation_start_time) / 1000.0).round(1)} seconds" if @simulation_start_time
end
end
end
end
end

View File

@@ -2,7 +2,7 @@ module TAC
class TACNET
class Packet
PROTOCOL_VERSION = 1
PROTOCOL_HEADER_SEPERATOR = "|"
PROTOCOL_SEPERATOR = "|"
PROTOCOL_HEARTBEAT = "heartbeat"
PACKET_TYPES = {
@@ -12,6 +12,11 @@ module TAC
download_config: 10,
upload_config: 11,
list_configs: 12,
select_config: 13,
add_config: 14,
update_config: 15,
delete_config: 16,
add_group: 20,
update_group: 21,
@@ -30,7 +35,7 @@ module TAC
slice = message.split("|", 4)
if slice.size < 4
warn "Failed to split packet along first 4 " + PROTOCOL_HEADER_SEPERATOR + ". Raw return: " + slice.to_s
warn "Failed to split packet along first 4 " + PROTOCOL_SEPERATOR + ". Raw return: " + slice.to_s
return nil
end
@@ -73,11 +78,11 @@ module TAC
def encode_header
string = ""
string += protocol_version.to_s
string += PROTOCOL_HEADER_SEPERATOR
string += PROTOCOL_SEPERATOR
string += PACKET_TYPES[type].to_s
string += PROTOCOL_HEADER_SEPERATOR
string += PROTOCOL_SEPERATOR
string += content_length.to_s
string += PROTOCOL_HEADER_SEPERATOR
string += PROTOCOL_SEPERATOR
return string
end

View File

@@ -10,6 +10,7 @@ module TAC
packet = Packet.from_stream(message)
if packet
log.i(TAG, "Received packet of type: #{packet.type}")
hand_off(packet)
else
log.d(TAG, "Rejected raw packet: #{message}")
@@ -24,10 +25,21 @@ module TAC
handle_heartbeat(packet)
when :error
handle_error(packet)
when :download_config
handle_download_config(packet)
when :upload_config
handle_upload_config(packet)
when :list_configs
handle_list_configs(packet)
when :select_config
handle_select_config(packet)
when :add_config
handle_add_config(packet)
when :update_config # rename config/file
handle_update_config(packet)
when :delete_config
handle_delete_config(packet)
else
log.d(TAG, "No hand off available for packet type: #{packet.type}")
end
@@ -45,11 +57,15 @@ module TAC
# TODO: Handle errors
def handle_error(packet)
if @host_is_a_connection
title, message = packet.body.split(Packet::PROTOCOL_SEPERATOR, 2)
$window.push_state(TAC::Dialog::TACNETDialog, title: title, message: message)
end
end
def handle_upload_config(packet)
begin
config_name, json = packet.body.split(Packet::PROTOCOL_HEADER_SEPERATOR)
config_name, json = packet.body.split(Packet::PROTOCOL_SEPERATOR, 2)
data = JSON.parse(json, symbolize_names: true)
if @host_is_a_connection
@@ -58,11 +74,15 @@ module TAC
$window.push_state(TAC::Dialog::AlertDialog, title: "Invalid Config", message: "Remote config to old.")
elsif data.is_a?(Hash) && data.dig(:config, :spec_version) == TAC::CONFIG_SPEC_VERSION
$window.push_state(TAC::Dialog::ConfirmDialog, title: "Replace Config", message: "Replace local config\nwith remote config?", callback_method: proc {
File.open("#{TAC::CONFIGS_PATH}/#{config_name}.json", "w") { |f| f.write json }
$window.backend.load_config(config_name)
})
if $window.backend.config.name == config_name
$window.backend.load_config(config_name)
$window.instance_variable_get(:"@states").each do |state|
state.populate_groups_list if state.is_a?(TAC::States::Editor)
end
end
elsif data.is_a?(Hash) && data.dig(:config, :spec_version) < TAC::CONFIG_SPEC_VERSION
# OLD CONFIG, Upgrade?
@@ -103,6 +123,56 @@ module TAC
end
end
def handle_list_configs(packet)
if @host_is_a_connection # Download new or updated configs
list = packet.body.split(Packet::PROTOCOL_SEPERATOR).map { |part| part.split(",") }
remote_configs = list.map { |l| l.first }
local_configs = Dir.glob("#{TAC::CONFIGS_PATH}/*.json").map { |f| File.basename(f, ".json") }
_diff = local_configs - remote_configs
list.each do |name, revision|
revision = Integer(revision)
path = "#{TAC::CONFIGS_PATH}/#{name}.json"
if File.exist?(path)
config = Config.new(name)
if config.configuration.revision < revision
$window.backend.tacnet.puts( PacketHandler.packet_download_config(name) )
elsif config.configuration.revision > revision
$window.backend.tacnet.puts( PacketHandler.packet_upload_config(name, JSON.dump( config )) )
end
else
$window.backend.tacnet.puts( PacketHandler.packet_download_config(name) )
end
end
_diff.each do |name|
config = Config.new(name)
$window.backend.tacnet.puts( PacketHandler.packet_upload_config(name, JSON.dump( config )) )
end
else
if $server.active_client && $server.active_client.connected?
$server.active_client.puts(PacketHandler.packet_list_configs)
end
end
end
def handle_select_config(packet)
end
def handle_add_config(packet)
end
def handle_update_config(packet)
end
def handle_delete_config(packet)
end
def self.packet_handshake(client_uuid)
Packet.create(Packet::PACKET_TYPES[:handshake], client_uuid)
end
@@ -120,10 +190,24 @@ module TAC
end
def self.packet_upload_config(config_name, json)
string = "#{config_name}#{Packet::PROTOCOL_HEADER_SEPERATOR}#{json.gsub("\n", " ")}"
string = "#{config_name}#{Packet::PROTOCOL_SEPERATOR}#{json.gsub("\n", " ")}"
Packet.create(Packet::PACKET_TYPES[:upload_config], string)
end
def self.packet_list_configs
files = Dir.glob("#{TAC::CONFIGS_PATH}/*.json")
list = files.map do |file|
name = File.basename(file, ".json")
config = Config.new(name)
"#{name},#{config.configuration.revision}"
end.join(Packet::PROTOCOL_SEPERATOR)
Packet.create(
Packet::PACKET_TYPES[:list_configs],
list
)
end
end
end
end
end

View File

@@ -71,11 +71,9 @@ module TAC
@active_client = client
# TODO: Backup local config
# SEND CONFIG
settings = TAC::Settings.new
config = File.read("#{TAC::CONFIGS_PATH}/#{settings.config}.json")
@active_client.puts(PacketHandler.packet_handshake(@active_client.uuid))
@active_client.puts(PacketHandler.packet_upload_config(settings.config, config))
@active_client.puts(PacketHandler.packet_list_configs)
log.i(TAG, "Client connected!")
@@ -137,4 +135,4 @@ module TAC
end
end
end
end
end

View File

@@ -46,8 +46,8 @@ module TAC
padding_top: THEME_ITEM_PADDING,
padding_bottom: THEME_ITEM_PADDING
}
THEME_EVEN_COLOR = Gosu::Color.new(0xff_606060)
THEME_ODD_COLOR = Gosu::Color.new(0xff_202020)
THEME_EVEN_COLOR = Gosu::Color.new(0xff_202020)
THEME_ODD_COLOR = Gosu::Color.new(0xff_606060)
THEME_CONTENT_BACKGROUND = Gosu::Color.new(0x88_007f3f)
THEME_HEADER_BACKGROUND = [
TAC::Palette::TIMECRAFTERS_PRIMARY, TAC::Palette::TIMECRAFTERS_PRIMARY,

BIN
media/error_alarm.ogg Normal file

Binary file not shown.

View File

@@ -26,6 +26,8 @@ require_relative "lib/dialogs/alert_dialog"
require_relative "lib/dialogs/confirm_dialog"
require_relative "lib/dialogs/name_prompt_dialog"
require_relative "lib/dialogs/variable_dialog"
require_relative "lib/dialogs/tacnet_dialog"
require_relative "lib/dialogs/tacnet_status_dialog"
require_relative "lib/tacnet"
require_relative "lib/tacnet/packet"
require_relative "lib/tacnet/packet_handler"