Skip to content

Instantly share code, notes, and snippets.

@simonmd
Last active May 26, 2020 16:33
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save simonmd/cc2ca1e5e94831013b93 to your computer and use it in GitHub Desktop.
Save simonmd/cc2ca1e5e94831013b93 to your computer and use it in GitHub Desktop.
DICOM Widget for Dashing

Description

Simple Dashing widget (and associated files) to display DICOM server checks.

##Dependencies

dicom

Add it to dashing's gemfile:

gem 'dicom'

and run bundle install.

##Usage

To use this widget, copy dicom.html, dicom.coffee, and dicom.scss into a /widgets/dicom directory. Put the dicom_check.rb file in your /jobs folder and the dicom_server.rb file in your /lib folder.

Since this widget iterates through the servers you define in dicom_server.rb, You must also make the dicom_servers array available in the views, as a sinatra helper. To do this, add this in your config.ru:

require './lib/dicom_server'

and then add a helper:

    def dicom_servers
      dicom_servers = DicomServer.all
    end

To include the widget in a dashboard, add the following snippet to the dashboard layout file:

  <% dicom_servers.each do |server| %>
    <li  data-row = "1" data-col = "1" data-sizex = "1" data-sizey = "1">
      <div data-view = "Dicom" data-id = '<%= server.id %>' data-title = '<%= server.name %>' data-ip = '<%= server.ip %>' data-weburl = '<%= server.web_url %>' data-configurl = '<%= server.config_url %>'></div>
    </li>
  <% end %>

##Settings

The servers to check must be configured in the lib/dicom_server.rb file as an array of hashes, configured like this:

  ############## CONFIGURATION ############
  PING_COUNT = 10 # Number of ping checks to perform
  DICOM.logger.level = Logger::ERROR # Log level for dicom communications
  CALLING_AE = "CURIOSITY" # The AET that will be used to identify the widget against DICOM hosts

  # Dicom servers to check
  # id: unique name to pass data to the widget through the data-id attribute
  # name: Name of the server to be displayed
  # ip: IP of the DICOM server to check
  # port: DICOM port of the server to check
  # aet: AE Title of the server to check
  # check_ping: flag if ping checks should be performed
  # check_echo: flags if DICOM checks should be performed
  # alert: flag to signal additional code to alert if checks failed (pending)
  # web_url: web url of the DICOM server's web interface, for easy access with the link icon on the widget
  # config_url: web url of the DICOM server's web interface, for easy access with the link icon on the widget
  DICOM_SERVERS = [
    {id: "demo", name: "Demo", ip: "10.10.0.1", port: 11112, aet: "EDX-PACS", check_ping: true, check_echo: true, alert: false, web_url: "http://10.10.0.1:8080/edx-pacs", config_url: "http://10.10.0.1:8080/jmx-console"},
    {id: "tdx", name: "TDX", ip: "10.13.0.1", port: 11112, aet: "EDX-PACS", check_ping: true, check_echo: true, alert: false, web_url: "http://10.13.0.1:8080/edx-pacs", config_url: "http://10.13.0.1:8080/jmx-console"}
  ]
  ############ END CONFIGURATION ###########
class Dashing.Dicom extends Dashing.Widget
ready: ->
onData: (data) ->
# Flash widget when data comes in
$(@node).fadeOut().fadeIn()
# Set color according to ping and echo status
# ping and echo true: green
# ping true, echo false or nil: orange
# ping false: red
color = if (data.result_ping == true && data.result_echo == true) then "#96BF48" else if (data.result_ping == true && (data.result_echo == false || data.result_echo == null)) then "#F08B0E" else "#BF4848"
# Change background color of widget
$(@get('node')).css('background-color', "#{color}")
<h1 class = "title" data-bind = "title"></h1>
<p class = "ip" data-bind = "ip"></p>
<p class = "updated-at" data-bind = "updatedAtMessage"></p>
<a data-bind-href = "weburl"><i class = " icon-link"></i></a>
&nbsp;&nbsp;
<a data-bind-href = "configurl"><i class = "icon-cog"></i></a>
// ----------------------------------------------------------------------------
// Widget Styles
// ----------------------------------------------------------------------------
.widget-dicom {
background-color: #12b0c5;
vertical-align: top;
.title {
color: rgba(255, 255, 255, 0.9);
}
.ip {
color: rgba(255, 255, 255, 0.6);
}
.updated-at {
color: rgba(0, 0, 0, 0.3);
}
}
# Dicom check Job for Dashing
# Get all dicom servers
dicom_servers = DicomServer.all
# Start job
SCHEDULER.every '1m', first_in: 0 do |job|
# Iterate for each server
dicom_servers.each do |server|
############ PING ############
if server.ping? == false# Ping failed
if server.ping_status == true # was ping working on previous check?
# This is a new fail, alert failure
puts "#{DateTime.now} - [#{server.name}] New ping failure"
#notifier.ping "Se ha detectado una falla en el servidor: #{server[:server.name]}"
end
result_ping = false # Set current ping status result
else # Ping succeded
if server.ping_status == false # was ping not working on previous check?
# This is a recovered failure, alert recovery
puts "#{DateTime.now} - [#{server.name}] Ping failure recovery"
#notifier.ping "Failure recovery on server: #{server[:server.name]}"
end
result_ping = true # Set current ping status result
############ DICOM ECHO ############
# Only makes sense to test DICOM if there is a ping
# There may be some edge cases where AE does not rspond to ping but does respond to DICOM
# We won't consider that here, we will assume that AE can respond to both
if server.echo? == false# echo failed
if server.echo_status == true # was echo working on previous check?
# This is a new fail, alert failure
puts "#{DateTime.now} - [#{server.name}] New DICOM echo failure"
#notifier.echo "Se ha detectado una falla en el servidor: #{server[:server.name]}"
end
result_echo = false # Set current echo status result
else # echo succeded
if server.echo_status == false # was echo not working on previous check?
# This is a recovered failure, alert recovery
puts "#{DateTime.now} - [#{server.name}] DICOM Echo failure recovery"
#notifier.echo "Failure recovery on server: #{server[:server.name]}"
end
result_echo = true # Set current echo status result
end
end
# Set previous check status for next check
server.ping_status = result_ping
server.echo_status = result_echo
# Log checks in history
puts "#{DateTime.now} - [#{server.name}] Check finished: Ping: #{result_ping}, DICOM Echo: #{result_echo}"
# puts "#{DateTime.now} - [#{server.name}] Status: Ping: #{server.ping_status}, DICOM Echo: #{server.echo_status}"
# Send the result of the events to the dashboard
send_event(server.id, result_ping: result_ping, result_echo: result_echo)
end
end
#!/usr/bin/env ruby
require 'ostruct'
require 'dicom'
include DICOM
############## CONFIGURATION ############
PING_COUNT = 10 # Number of ping checks to perform
DICOM.logger.level = Logger::ERROR # Log level for dicom communications
CALLING_AE = "CURIOSITY" # The AET that will be used to identify the widget against DICOM hosts
# Dicom servers to check
# id: unique name to pass data to the widget through the data-id attribute
# name: Name of the server to be displayed
# ip: IP of the DICOM server to check
# port: DICOM port of the server to check
# aet: AE Title of the server to check
# check_ping: flag if ping checks should be performed
# check_echo: flags if DICOM checks should be performed
# alert: flag to signal additional code to alert if checks failed (pending)
# web_url: web url of the DICOM server's web interface, for easy access with the link icon on the widget
# config_url: web url of the DICOM server's web interface, for easy access with the link icon on the widget
DICOM_SERVERS = [
{id: "demo", name: "Demo", ip: "10.10.0.1", port: 11112, aet: "EDX-PACS", check_ping: true, check_echo: true, alert: false, web_url: "http://10.10.0.1:8080/edx-pacs", config_url: "http://10.10.0.1:8080/jmx-console"},
{id: "tdx", name: "TDX", ip: "10.13.0.1", port: 11112, aet: "EDX-PACS", check_ping: true, check_echo: true, alert: false, web_url: "http://10.13.0.1:8080/edx-pacs", config_url: "http://10.13.0.1:8080/jmx-console"}
]
############ END CONFIGURATION ###########
class DicomServer < OpenStruct
def ping?
# Call a standard ping from command
system("ping -q -c #{PING_COUNT} #{self.ip} > /dev/null 2>&1") # Supress output
if $? == 0
# Ping exited with 0 status, successful
return true
else
# Ping exited with any other status, assume fail
return false
end
end
# Does server respond to DICOM echo?
def echo?
begin
# Call a DICOM echo with RubyDICOM's .test (will return true/false)
DClient.new(self.ip, self.port, {ae: CALLING_AE, host_ae: self.aet}).test
rescue *[Timeout::Error, Errno::ETIMEDOUT, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, SocketError]
return false # Handle connection errors, assume false
end
end
# Return all dicom servers
def self.all
# Load servers from server array of hashes
dicom_servers = []
DICOM_SERVERS.each do |s|
dicom_servers << self.new(s)
end
return dicom_servers
end
end

The MIT License (MIT)

Copyright (c) 2015 Instituto de Alta Tecnologia Medica - IATM

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment