21 from subprocess
import Popen, PIPE
26 from diagnostic_msgs.msg
import DiagnosticArray, DiagnosticStatus, KeyValue
30 rospy.init_node(
"ntp_monitor")
33 stat = DiagnosticStatus()
34 stat.level = DiagnosticStatus.WARN
36 stat.message =
'No Data' 39 self.
msg = DiagnosticArray()
40 self.msg.header.stamp = rospy.get_rostime()
41 self.msg.status = [stat]
45 self.
diag_pub = rospy.Publisher(
"/diagnostics", DiagnosticArray, queue_size=1)
50 stat = DiagnosticStatus()
51 stat.level = DiagnosticStatus.WARN
53 stat.message =
'No Data' 59 p = Popen([
"ntpdate",
"-q", host], stdout=PIPE, stdin=PIPE, stderr=PIPE)
61 stdout, stderr = p.communicate()
63 stdout = stdout.decode()
64 except (UnicodeDecodeError, AttributeError):
68 st.level = DiagnosticStatus.ERROR
69 st.message =
'ntpdate Error' 70 st.values = [ KeyValue(key =
'ntpdate Error', value = stderr),
71 KeyValue(key =
'Output', value = stdout) ]
74 measured_offset = float(re.search(
"offset (.*),", stdout).group(1))*1000000
76 st.level = DiagnosticStatus.OK
78 st.values = [ KeyValue(
"NTP Server" , self.
ntp_server),
79 KeyValue(
"Offset (us)", str(measured_offset)),
80 KeyValue(
"Offset tolerance (us)", str(off)),
81 KeyValue(
"Offset tolerance (us) for Error", str(self.
error_offset)) ]
83 if (abs(measured_offset) > off):
84 st.level = DiagnosticStatus.WARN
85 st.message =
"NTP Offset Too High" 87 st.level = DiagnosticStatus.ERROR
88 st.message =
"NTP Offset Too High" 90 except Exception
as e:
91 stat.level = DiagnosticStatus.ERROR
92 stat.message =
'ntpdate Exception' 93 stat.values = [ KeyValue(key =
'Exception', value = str(e)) ]
95 self.
msg = DiagnosticArray()
96 self.msg.header.stamp = rospy.get_rostime()
97 self.msg.status = [stat]
100 self.diag_pub.publish(self.
msg)
104 parser = optparse.OptionParser(usage=
"usage: ntp_monitor ntp-hostname []")
105 parser.add_option(
"--offset", dest=
"offset",
106 action=
"store", default=500,
107 help=
"Offset from NTP host", metavar=
"OFFSET")
108 parser.add_option(
"--error-offset", dest=
"error_offset",
109 action=
"store", default=5000000,
110 help=
"Offset from NTP host. Above this is error", metavar=
"OFFSET")
111 parser.add_option(
"--diag-hostname", dest=
"diag_hostname",
112 help=
"Computer name in diagnostics output (ex: 'c1')",
113 metavar=
"DIAG_HOSTNAME",
114 action=
"store", default=
None)
115 options, args = parser.parse_args(rospy.myargv())
118 parser.error(
"Invalid arguments. Must have HOSTNAME [args]. %s" % args)
119 print(
'Invalid arguments.')
123 offset = int(options.offset)
124 error_offset = int(options.error_offset)
126 parser.error(
"Offsets must be numbers")
127 print(
'Offsets must be numbers')
132 hostname = socket.gethostname()
136 self.
offset = rospy.get_param(
'~offset', offset)
140 if __name__ ==
"__main__":
def publish_diagnostics(self, event)
def update_diagnostics(self, event=None)
def parse_args(self, argv=sys.argv)
def __init__(self, argv=sys.argv)