Fix bad GNSS status reported by PTP tracker

A "FAILURE-NOFIX" GNSS status was being reported because the new ICE
driver reports "locked_ho_acq" status instead of "locked_ho_ack" as
before. Now the logic takes both variations into account to decide upon
the GNSS status.

BONUS: Found an issue (bad indentation with subsequent logic error) with
the threading lockers for ptp4l and ts2phc handlers in the sidecar
(notificationclient), that could lead to an exception.

Test Plan:
PASS: Checked with synchronized GNSS status.

Closes-Bug: #1996179
Signed-off-by: Douglas Henrique Koerich <douglashenrique.koerich@windriver.com>
Change-Id: I1ad3c4c9ad9e1428685bf6729c77ba29b470ece3
This commit is contained in:
Douglas Henrique Koerich 2022-11-10 11:26:14 -03:00
parent db31ab00ba
commit 3a365e737a
3 changed files with 265 additions and 201 deletions

View File

@ -32,6 +32,7 @@ CLOCK_CLASS_VALUE6 = "6"
NMEA_SERIALPORT = "ts2phc.nmea_serialport" NMEA_SERIALPORT = "ts2phc.nmea_serialport"
GNSS_PIN = "GNSS-1PPS" GNSS_PIN = "GNSS-1PPS"
GNSS_LOCKED_HO_ACK = 'locked_ho_ack' GNSS_LOCKED_HO_ACK = 'locked_ho_ack'
GNSS_LOCKED_HO_ACQ = 'locked_ho_acq'
GNSS_DPLL_0 = "DPLL0" GNSS_DPLL_0 = "DPLL0"
GNSS_DPLL_1 = "DPLL1" GNSS_DPLL_1 = "DPLL1"

View File

@ -68,11 +68,15 @@ class GnssMonitor(Observer):
} }
# Initialize status # Initialize status
if self.gnss_cgu_handler.cgu_output_parsed['EEC DPLL']['Current reference'] == 'GNSS-1PPS': if self.gnss_cgu_handler.cgu_output_parsed[
self.gnss_eec_state = self.gnss_cgu_handler.cgu_output_parsed['EEC DPLL']['Status'] 'EEC DPLL']['Current reference'] == 'GNSS-1PPS':
self.gnss_eec_state = \
self.gnss_cgu_handler.cgu_output_parsed['EEC DPLL']['Status']
if self.gnss_cgu_handler.cgu_output_parsed['PPS DPLL']['Current reference'] == 'GNSS-1PPS': if self.gnss_cgu_handler.cgu_output_parsed[
self.gnss_pps_state = self.gnss_cgu_handler.cgu_output_parsed['PPS DPLL']['Status'] 'PPS DPLL']['Current reference'] == 'GNSS-1PPS':
self.gnss_pps_state = \
self.gnss_cgu_handler.cgu_output_parsed['PPS DPLL']['Status']
def update(self, subject, matched_line) -> None: def update(self, subject, matched_line) -> None:
LOG.info("Kernel event detected. %s" % matched_line) LOG.info("Kernel event detected. %s" % matched_line)
@ -92,11 +96,16 @@ class GnssMonitor(Observer):
self.gnss_cgu_handler.cgu_output_to_dict() self.gnss_cgu_handler.cgu_output_to_dict()
self.gnss_eec_state = self.gnss_eec_state = \ self.gnss_eec_state = self.gnss_eec_state = \
self.gnss_cgu_handler.cgu_output_parsed['EEC DPLL']['Status'] self.gnss_cgu_handler.cgu_output_parsed['EEC DPLL']['Status']
self.gnss_pps_state = self.gnss_cgu_handler.cgu_output_parsed['PPS DPLL']['Status'] self.gnss_pps_state = \
self.gnss_cgu_handler.cgu_output_parsed['PPS DPLL']['Status']
LOG.debug("GNSS EEC Status is: %s" % self.gnss_eec_state) LOG.debug("GNSS EEC Status is: %s" % self.gnss_eec_state)
LOG.debug("GNSS PPS Status is: %s" % self.gnss_pps_state) LOG.debug("GNSS PPS Status is: %s" % self.gnss_pps_state)
if self.gnss_pps_state == 'locked_ho_ack' and \ if self.gnss_pps_state in [
self.gnss_eec_state == 'locked_ho_ack': constants.GNSS_LOCKED_HO_ACK,
constants.GNSS_LOCKED_HO_ACQ] and \
self.gnss_eec_state in [
constants.GNSS_LOCKED_HO_ACK,
constants.GNSS_LOCKED_HO_ACQ]:
self._state = GnssState.Synchronized self._state = GnssState.Synchronized
else: else:
self._state = GnssState.Failure_Nofix self._state = GnssState.Failure_Nofix

View File

@ -33,23 +33,32 @@ THIS_NODE_NAME = os.environ.get("THIS_NODE_NAME", 'controller-0')
# Event source to event type mapping # Event source to event type mapping
source_type = { source_type = {
'/sync/gnss-status/gnss-sync-status': 'event.sync.gnss-status.gnss-state-change', '/sync/gnss-status/gnss-sync-status':
'/sync/ptp-status/clock-class': 'event.sync.ptp-status.ptp-clock-class-change', 'event.sync.gnss-status.gnss-state-change',
'/sync/ptp-status/lock-state': 'event.sync.ptp-status.ptp-state-change', '/sync/ptp-status/clock-class':
'/sync/sync-status/os-clock-sync-state': 'event.sync.sync-status.os-clock-sync-state-change', 'event.sync.ptp-status.ptp-clock-class-change',
'/sync/sync-status/sync-state': 'event.sync.sync-status.synchronization-state-change', '/sync/ptp-status/lock-state':
'/sync/synce-status/clock-quality': 'event.sync.synce-status.synce-clock-quality-change', 'event.sync.ptp-status.ptp-state-change',
'/sync/synce-status/lock-state-extended': 'event.sync.synce-status.synce-state-change-extended', '/sync/sync-status/os-clock-sync-state':
'/sync/synce-status/lock-state': 'event.sync.synce-status.synce-state-change', 'event.sync.sync-status.os-clock-sync-state-change',
'/sync/sync-status/sync-state':
'event.sync.sync-status.synchronization-state-change',
'/sync/synce-status/clock-quality':
'event.sync.synce-status.synce-clock-quality-change',
'/sync/synce-status/lock-state-extended':
'event.sync.synce-status.synce-state-change-extended',
'/sync/synce-status/lock-state':
'event.sync.synce-status.synce-state-change',
} }
'''Entry point of Default Process Worker''' '''Entry point of Default Process Worker'''
def ProcessWorkerDefault(event, sqlalchemy_conf_json, broker_transport_endpoint): def ProcessWorkerDefault(event, sqlalchemy_conf_json,
worker = PtpWatcherDefault(event, sqlalchemy_conf_json, broker_transport_endpoint) broker_transport_endpoint):
worker = PtpWatcherDefault(event, sqlalchemy_conf_json,
broker_transport_endpoint)
worker.run() worker.run()
return
class PtpWatcherDefault: class PtpWatcherDefault:
@ -79,8 +88,9 @@ class PtpWatcherDefault:
self.init_time = time.time() self.init_time = time.time()
self.daemon_context = daemon_context self.daemon_context = daemon_context
def _build_event_response(self, resource_path, last_event_time, resource_address, def _build_event_response(
sync_state, value_type=constants.VALUE_TYPE_ENUMERATION): self, resource_path, last_event_time, resource_address,
sync_state, value_type=constants.VALUE_TYPE_ENUMERATION):
if resource_path in [constants.SOURCE_SYNC_PTP_CLOCK_CLASS, if resource_path in [constants.SOURCE_SYNC_PTP_CLOCK_CLASS,
constants.SOURCE_SYNCE_CLOCK_QUALITY]: constants.SOURCE_SYNCE_CLOCK_QUALITY]:
data_type = constants.DATA_TYPE_METRIC data_type = constants.DATA_TYPE_METRIC
@ -111,123 +121,123 @@ class PtpWatcherDefault:
resource_address = rpc_kwargs.get('ResourceAddress', None) resource_address = rpc_kwargs.get('ResourceAddress', None)
optional = rpc_kwargs.get('optional', None) optional = rpc_kwargs.get('optional', None)
if resource_address: if resource_address:
_, nodename, resource_path = utils.parse_resource_address(resource_address) _, nodename, resource_path = utils.parse_resource_address(
resource_address)
if resource_path == constants.SOURCE_SYNC_ALL: if resource_path == constants.SOURCE_SYNC_ALL:
resource_path = constants.SOURCE_SYNC_SYNC_STATE resource_path = constants.SOURCE_SYNC_SYNC_STATE
if resource_path == constants.SOURCE_SYNC_GNSS_SYNC_STATUS: if resource_path == constants.SOURCE_SYNC_GNSS_SYNC_STATUS:
self.watcher.gnsstracker_context_lock.acquire() self.watcher.gnsstracker_context_lock.acquire()
if optional: if optional:
sync_state = self.watcher.gnsstracker_context[optional]. \ sync_state = \
get('sync_state', GnssState.Failure_Nofix) self.watcher.gnsstracker_context[optional].get(
last_event_time = self.watcher.gnsstracker_context[optional].get( 'sync_state', GnssState.Failure_Nofix)
'last_event_time', last_event_time = \
time.time()) self.watcher.gnsstracker_context[optional].get(
lastStatus[optional] = self._build_event_response(resource_path, 'last_event_time', time.time())
last_event_time, lastStatus[optional] = self._build_event_response(
resource_address, resource_path, last_event_time, resource_address,
sync_state) sync_state)
else: else:
for config in self.daemon_context['GNSS_INSTANCES']: for config in self.daemon_context['GNSS_INSTANCES']:
sync_state = self.watcher.gnsstracker_context[config] \ sync_state = \
.get('sync_state', GnssState.Failure_Nofix) self.watcher.gnsstracker_context[config].get(
last_event_time = self.watcher.gnsstracker_context[config].get( 'sync_state', GnssState.Failure_Nofix)
'last_event_time', last_event_time = \
time.time()) self.watcher.gnsstracker_context[config].get(
lastStatus[config] = self._build_event_response(resource_path, 'last_event_time', time.time())
last_event_time, lastStatus[config] = self._build_event_response(
resource_address, resource_path, last_event_time,
sync_state) resource_address, sync_state)
self.watcher.gnsstracker_context_lock.release() self.watcher.gnsstracker_context_lock.release()
elif resource_path == constants.SOURCE_SYNC_PTP_CLOCK_CLASS: elif resource_path == constants.SOURCE_SYNC_PTP_CLOCK_CLASS:
self.watcher.ptptracker_context_lock.acquire() self.watcher.ptptracker_context_lock.acquire()
if optional: if optional:
clock_class = self.watcher.ptptracker_context[optional].get('clock_class', clock_class = \
'248') self.watcher.ptptracker_context[optional].get(
last_clock_class_event_time = self.watcher.ptptracker_context[optional].get( 'clock_class', '248')
'last_clock_class_event_time', last_clock_class_event_time = \
time.time()) self.watcher.ptptracker_context[optional].get(
lastStatus[optional] = \ 'last_clock_class_event_time', time.time())
self._build_event_response(resource_path, lastStatus[optional] = self._build_event_response(
last_clock_class_event_time, resource_path, last_clock_class_event_time,
resource_address, resource_address, clock_class,
clock_class, constants.VALUE_TYPE_METRIC)
constants.VALUE_TYPE_METRIC)
else: else:
for config in self.daemon_context['PTP4L_INSTANCES']: for config in self.daemon_context['PTP4L_INSTANCES']:
clock_class = self.watcher.ptptracker_context[config].get('clock_class', clock_class = \
'248') self.watcher.ptptracker_context[config].get(
'clock_class', '248')
last_clock_class_event_time = \ last_clock_class_event_time = \
self.watcher.ptptracker_context[config].get( self.watcher.ptptracker_context[config].get(
'last_clock_class_event_time', 'last_clock_class_event_time',
time.time()) time.time())
lastStatus[config] = \ lastStatus[config] = self._build_event_response(
self._build_event_response(resource_path, resource_path, last_clock_class_event_time,
last_clock_class_event_time, resource_address, clock_class,
resource_address, constants.VALUE_TYPE_METRIC)
clock_class,
constants.VALUE_TYPE_METRIC)
self.watcher.ptptracker_context_lock.release() self.watcher.ptptracker_context_lock.release()
elif resource_path == constants.SOURCE_SYNC_PTP_LOCK_STATE: elif resource_path == constants.SOURCE_SYNC_PTP_LOCK_STATE:
self.watcher.ptptracker_context_lock.acquire() self.watcher.ptptracker_context_lock.acquire()
if optional: if optional:
sync_state = self.watcher.ptptracker_context[optional].get('sync_state', sync_state = \
PtpState.Freerun) self.watcher.ptptracker_context[optional].get(
last_event_time = self.watcher.ptptracker_context[optional].get( 'sync_state', PtpState.Freerun)
'last_event_time', last_event_time = \
time.time()) self.watcher.ptptracker_context[optional].get(
lastStatus[optional] = self._build_event_response(resource_path, 'last_event_time', time.time())
last_event_time, lastStatus[optional] = self._build_event_response(
resource_address, resource_path, last_event_time, resource_address,
sync_state) sync_state)
else: else:
for config in self.daemon_context['PTP4L_INSTANCES']: for config in self.daemon_context['PTP4L_INSTANCES']:
sync_state = \ sync_state = \
self.watcher.ptptracker_context[config].get('sync_state', self.watcher.ptptracker_context[config].get(
PtpState.Freerun) 'sync_state', PtpState.Freerun)
last_event_time = self.watcher.ptptracker_context[config].get( last_event_time = \
'last_event_time', self.watcher.ptptracker_context[config].get(
time.time()) 'last_event_time', time.time())
lastStatus[config] = self._build_event_response(resource_path, lastStatus[config] = self._build_event_response(
last_event_time, resource_path, last_event_time,
resource_address, resource_address, sync_state)
sync_state)
self.watcher.ptptracker_context_lock.release() self.watcher.ptptracker_context_lock.release()
elif resource_path == constants.SOURCE_SYNC_OS_CLOCK: elif resource_path == constants.SOURCE_SYNC_OS_CLOCK:
self.watcher.osclocktracker_context_lock.acquire() self.watcher.osclocktracker_context_lock.acquire()
sync_state = self.watcher.osclocktracker_context.get('sync_state', sync_state = \
OsClockState.Freerun) self.watcher.osclocktracker_context.get(
last_event_time = self.watcher.osclocktracker_context.get('last_event_time', 'sync_state', OsClockState.Freerun)
time.time()) last_event_time = \
self.watcher.osclocktracker_context.get(
'last_event_time', time.time())
self.watcher.osclocktracker_context_lock.release() self.watcher.osclocktracker_context_lock.release()
lastStatus['os_clock_status'] = self._build_event_response(resource_path, lastStatus['os_clock_status'] = self._build_event_response(
last_event_time, resource_path, last_event_time, resource_address,
resource_address, sync_state)
sync_state)
elif resource_path == constants.SOURCE_SYNC_SYNC_STATE: elif resource_path == constants.SOURCE_SYNC_SYNC_STATE:
self.watcher.overalltracker_context_lock.acquire() self.watcher.overalltracker_context_lock.acquire()
sync_state = self.watcher.overalltracker_context.get('sync_state', sync_state = self.watcher.overalltracker_context.get(
OverallClockState.Freerun) 'sync_state', OverallClockState.Freerun)
last_event_time = self.watcher.overalltracker_context.get('last_event_time', last_event_time = self.watcher.overalltracker_context.get(
time.time()) 'last_event_time', time.time())
self.watcher.overalltracker_context_lock.release() self.watcher.overalltracker_context_lock.release()
lastStatus['overall_sync_status'] = self._build_event_response(resource_path, lastStatus['overall_sync_status'] = \
last_event_time, self._build_event_response(
resource_address, resource_path, last_event_time, resource_address,
sync_state) sync_state)
LOG.debug("query_status: {}".format(lastStatus)) LOG.debug("query_status: {}".format(lastStatus))
else: else:
# Request is for PTP v1 notification # Request is for PTP v1 notification
# PTP v1 only supports single instance ptp # PTP v1 only supports single instance ptp
instance = self.daemon_context['PTP4L_INSTANCES'][0] instance = self.daemon_context['PTP4L_INSTANCES'][0]
if len(self.daemon_context['PTP4L_INSTANCES']) > 1: if len(self.daemon_context['PTP4L_INSTANCES']) > 1:
LOG.warning( LOG.warning("Multiple ptp4l instances configured, "
"Multiple ptp4l instances configured, retrieving status for %s" % instance) "retrieving status for %s" % instance)
self.watcher.ptptracker_context_lock.acquire() self.watcher.ptptracker_context_lock.acquire()
sync_state = self.watcher.ptptracker_context[instance].get('sync_state', sync_state = self.watcher.ptptracker_context[instance].get(
PtpState.Freerun) 'sync_state', PtpState.Freerun)
last_event_time = self.watcher.ptptracker_context[instance].get('last_event_time', last_event_time = \
time.time()) self.watcher.ptptracker_context[instance].get(
'last_event_time', time.time())
lastStatus[constants.PTP_V1_KEY] = { lastStatus[constants.PTP_V1_KEY] = {
'ResourceType': ResourceType.TypePTP, 'ResourceType': ResourceType.TypePTP,
'EventData': { 'EventData': {
@ -246,7 +256,6 @@ class PtpWatcherDefault:
def trigger_delivery(self, **rpc_kwargs): def trigger_delivery(self, **rpc_kwargs):
self.watcher.forced_publishing = True self.watcher.forced_publishing = True
self.watcher.signal_ptp_event() self.watcher.signal_ptp_event()
pass
def __init__(self, event, sqlalchemy_conf_json, daemon_context_json): def __init__(self, event, sqlalchemy_conf_json, daemon_context_json):
self.sqlalchemy_conf = json.loads(sqlalchemy_conf_json) self.sqlalchemy_conf = json.loads(sqlalchemy_conf_json)
@ -258,92 +267,109 @@ class PtpWatcherDefault:
# PTP Context # PTP Context
self.ptptracker_context = {} self.ptptracker_context = {}
for config in self.daemon_context['PTP4L_INSTANCES']: for config in self.daemon_context['PTP4L_INSTANCES']:
self.ptptracker_context[config] = PtpWatcherDefault.DEFAULT_PTPTRACKER_CONTEXT.copy() self.ptptracker_context[config] = \
PtpWatcherDefault.DEFAULT_PTPTRACKER_CONTEXT.copy()
self.ptptracker_context[config]['sync_state'] = PtpState.Freerun self.ptptracker_context[config]['sync_state'] = PtpState.Freerun
self.ptptracker_context[config]['last_event_time'] = self.init_time self.ptptracker_context[config]['last_event_time'] = self.init_time
self.ptptracker_context[config]['holdover_seconds'] = os.environ.get("PTP_HOLDOVER_SECONDS", 30) self.ptptracker_context[config]['holdover_seconds'] = \
self.ptptracker_context[config]['poll_freq_seconds'] = os.environ.get( os.environ.get("PTP_HOLDOVER_SECONDS", 30)
"CONTROL_TIMEOUT", 2) self.ptptracker_context[config]['poll_freq_seconds'] = \
self.ptp_device_simulated = "true" == self.ptptracker_context[config].get( os.environ.get("CONTROL_TIMEOUT", 2)
'device_simulated', self.ptp_device_simulated = \
"False") "true" == self.ptptracker_context[config].get(
self.ptptracker_context_lock = threading.Lock() 'device_simulated', "False")
self.ptptracker_context_lock = threading.Lock()
LOG.debug("ptptracker_context: %s" % self.ptptracker_context) LOG.debug("ptptracker_context: %s" % self.ptptracker_context)
# GNSS Context # GNSS Context
self.gnsstracker_context = {} self.gnsstracker_context = {}
for config in self.daemon_context['GNSS_INSTANCES']: for config in self.daemon_context['GNSS_INSTANCES']:
self.gnsstracker_context[config] = PtpWatcherDefault.DEFAULT_GNSSTRACKER_CONTEXT.copy() self.gnsstracker_context[config] = \
self.gnsstracker_context[config]['sync_state'] = GnssState.Failure_Nofix PtpWatcherDefault.DEFAULT_GNSSTRACKER_CONTEXT.copy()
self.gnsstracker_context[config]['last_event_time'] = self.init_time self.gnsstracker_context[config]['sync_state'] = \
self.gnsstracker_context[config]['holdover_seconds'] = os.environ.get("GNSS_HOLDOVER_SECONDS", 30) GnssState.Failure_Nofix
self.gnsstracker_context[config]['poll_freq_seconds'] = os.environ.get( self.gnsstracker_context[config]['last_event_time'] = \
"CONTROL_TIMEOUT", 2) self.init_time
self.gnsstracker_context_lock = threading.Lock() self.gnsstracker_context[config]['holdover_seconds'] = \
os.environ.get("GNSS_HOLDOVER_SECONDS", 30)
self.gnsstracker_context[config]['poll_freq_seconds'] = \
os.environ.get("CONTROL_TIMEOUT", 2)
self.gnsstracker_context_lock = threading.Lock()
LOG.debug("gnsstracker_context: %s" % self.gnsstracker_context) LOG.debug("gnsstracker_context: %s" % self.gnsstracker_context)
# OS Clock Context # OS Clock Context
self.osclocktracker_context = {} self.osclocktracker_context = {}
self.osclocktracker_context = PtpWatcherDefault.DEFAULT_OS_CLOCK_TRACKER_CONTEXT.copy() self.osclocktracker_context = \
PtpWatcherDefault.DEFAULT_OS_CLOCK_TRACKER_CONTEXT.copy()
self.osclocktracker_context['sync_state'] = OsClockState.Freerun self.osclocktracker_context['sync_state'] = OsClockState.Freerun
self.osclocktracker_context['last_event_time'] = self.init_time self.osclocktracker_context['last_event_time'] = self.init_time
self.osclocktracker_context['holdover_seconds'] = os.environ.get("OS_CLOCK_HOLDOVER_SECONDS", 30) self.osclocktracker_context['holdover_seconds'] = \
self.osclocktracker_context['poll_freq_seconds'] = os.environ.get( os.environ.get("OS_CLOCK_HOLDOVER_SECONDS", 30)
"CONTROL_TIMEOUT", 2) self.osclocktracker_context['poll_freq_seconds'] = \
os.environ.get("CONTROL_TIMEOUT", 2)
self.osclocktracker_context_lock = threading.Lock() self.osclocktracker_context_lock = threading.Lock()
# Overall Sync Context # Overall Sync Context
self.overalltracker_context = {} self.overalltracker_context = {}
self.overalltracker_context = PtpWatcherDefault.DEFAULT_OVERALL_SYNC_TRACKER_CONTEXT.copy() self.overalltracker_context = \
PtpWatcherDefault.DEFAULT_OVERALL_SYNC_TRACKER_CONTEXT.copy()
self.overalltracker_context['sync_state'] = OverallClockState.Freerun self.overalltracker_context['sync_state'] = OverallClockState.Freerun
self.overalltracker_context['last_event_time'] = self.init_time self.overalltracker_context['last_event_time'] = self.init_time
self.overalltracker_context['holdover_seconds'] = os.environ.get("OVERALL_HOLDOVER_SECONDS", 30) self.overalltracker_context['holdover_seconds'] = \
self.overalltracker_context['poll_freq_seconds'] = os.environ.get( os.environ.get("OVERALL_HOLDOVER_SECONDS", 30)
"CONTROL_TIMEOUT", 2) self.overalltracker_context['poll_freq_seconds'] = \
os.environ.get("CONTROL_TIMEOUT", 2)
self.overalltracker_context_lock = threading.Lock() self.overalltracker_context_lock = threading.Lock()
self.event_timeout = float(os.environ.get('CONTROL_TIMEOUT', 2)) self.event_timeout = float(os.environ.get('CONTROL_TIMEOUT', 2))
self.node_name = self.daemon_context['THIS_NODE_NAME'] self.node_name = self.daemon_context['THIS_NODE_NAME']
self.namespace = self.daemon_context.get('THIS_NAMESPACE', 'notification') self.namespace = self.daemon_context.get(
'THIS_NAMESPACE', 'notification')
broker_transport_endpoint = self.daemon_context['NOTIFICATION_TRANSPORT_ENDPOINT'] broker_transport_endpoint = \
self.daemon_context['NOTIFICATION_TRANSPORT_ENDPOINT']
registration_transport_endpoint = self.daemon_context['REGISTRATION_TRANSPORT_ENDPOINT'] registration_transport_endpoint = \
self.daemon_context['REGISTRATION_TRANSPORT_ENDPOINT']
self.broker_endpoint = RpcEndpointInfo(broker_transport_endpoint) self.broker_endpoint = RpcEndpointInfo(broker_transport_endpoint)
self.registration_broker_endpoint = RpcEndpointInfo(registration_transport_endpoint) self.registration_broker_endpoint = \
RpcEndpointInfo(registration_transport_endpoint)
self.ptpeventproducer = PtpEventProducer( self.ptpeventproducer = PtpEventProducer(
self.node_name, self.node_name,
self.broker_endpoint.TransportEndpoint, self.broker_endpoint.TransportEndpoint,
self.registration_broker_endpoint.TransportEndpoint) self.registration_broker_endpoint.TransportEndpoint)
self.__ptprequest_handler = PtpWatcherDefault.PtpRequestHandlerDefault(self, self.__ptprequest_handler = \
self.daemon_context) PtpWatcherDefault.PtpRequestHandlerDefault(
self, self.daemon_context)
# Set forced_publishing to True so that initial states are published # Set forced_publishing to True so that initial states are published
# Main loop in run() sets it to false after the first iteration # Main loop in run() sets it to false after the first iteration
self.forced_publishing = True self.forced_publishing = True
self.observer_list = [GnssMonitor(i) for i in self.daemon_context['GNSS_CONFIGS']] self.observer_list = [
GnssMonitor(i) for i in self.daemon_context['GNSS_CONFIGS']]
# Setup OS Clock monitor # Setup OS Clock monitor
self.os_clock_monitor = OsClockMonitor(phc2sys_config=self.daemon_context['PHC2SYS_CONFIG']) self.os_clock_monitor = OsClockMonitor(
phc2sys_config=self.daemon_context['PHC2SYS_CONFIG'])
# Setup PTP Monitor(s) # Setup PTP Monitor(s)
self.ptp_monitor_list = [ self.ptp_monitor_list = [
PtpMonitor(config, self.ptptracker_context[config]['holdover_seconds'], PtpMonitor(config,
self.ptptracker_context[config]['holdover_seconds'],
self.ptptracker_context[config]['poll_freq_seconds'], self.ptptracker_context[config]['poll_freq_seconds'],
self.daemon_context['PHC2SYS_SERVICE_NAME']) for config in self.daemon_context['PHC2SYS_SERVICE_NAME'])
self.daemon_context['PTP4L_INSTANCES']] for config in self.daemon_context['PTP4L_INSTANCES']]
def signal_ptp_event(self): def signal_ptp_event(self):
if self.event: if self.event:
self.event.set() self.event.set()
else: else:
LOG.warning("Unable to assert ptp event") LOG.warning("Unable to assert ptp event")
pass
def run(self): def run(self):
# start location listener # start location listener
@ -364,7 +390,6 @@ class PtpWatcherDefault:
self.event.clear() self.event.clear()
else: else:
LOG.debug("daemon control event is timeout") LOG.debug("daemon control event is timeout")
pass
continue continue
self.__stop_listener() self.__stop_listener()
@ -376,27 +401,29 @@ class PtpWatcherDefault:
self.ptpeventproducer.start_status_listener( self.ptpeventproducer.start_status_listener(
self.__ptprequest_handler self.__ptprequest_handler
) )
return
def __stop_listener(self): def __stop_listener(self):
LOG.debug("stop listener to answer location querying") LOG.debug("stop listener to answer location querying")
self.ptpeventproducer.stop_status_listener(self.location_info) self.ptpeventproducer.stop_status_listener(self.location_info)
return
def __get_gnss_status(self, holdover_time, freq, sync_state, last_event_time, gnss_monitor): def __get_gnss_status(self, holdover_time, freq, sync_state,
last_event_time, gnss_monitor):
new_event, sync_state, new_event_time = gnss_monitor.get_gnss_status( new_event, sync_state, new_event_time = gnss_monitor.get_gnss_status(
holdover_time, freq, sync_state, last_event_time) holdover_time, freq, sync_state, last_event_time)
LOG.debug("Getting GNSS status.") LOG.debug("Getting GNSS status.")
return new_event, sync_state, new_event_time return new_event, sync_state, new_event_time
def __get_os_clock_status(self, holdover_time, freq, sync_state, last_event_time): def __get_os_clock_status(self, holdover_time, freq, sync_state,
new_event, sync_state, new_event_time = self.os_clock_monitor.os_clock_status( last_event_time):
holdover_time, freq, sync_state, last_event_time) new_event, sync_state, new_event_time = \
self.os_clock_monitor.os_clock_status(
holdover_time, freq, sync_state, last_event_time)
LOG.debug("Getting os clock status.") LOG.debug("Getting os clock status.")
return new_event, sync_state, new_event_time return new_event, sync_state, new_event_time
def __get_overall_sync_state(self, holdover_time, freq, sync_state, last_event_time): def __get_overall_sync_state(self, holdover_time, freq, sync_state,
last_event_time):
new_event = False new_event = False
new_event_time = last_event_time new_event_time = last_event_time
previous_sync_state = sync_state previous_sync_state = sync_state
@ -411,36 +438,44 @@ class PtpWatcherDefault:
LOG.debug("Getting overall sync state.") LOG.debug("Getting overall sync state.")
for gnss in self.observer_list: for gnss in self.observer_list:
if gnss._state == constants.UNKNOWN_PHC_STATE or gnss._state == GnssState.Failure_Nofix: if gnss._state == constants.UNKNOWN_PHC_STATE or \
gnss._state == GnssState.Failure_Nofix:
gnss_state = GnssState.Failure_Nofix gnss_state = GnssState.Failure_Nofix
elif gnss._state == GnssState.Synchronized and gnss_state != GnssState.Failure_Nofix: elif gnss._state == GnssState.Synchronized and \
gnss_state != GnssState.Failure_Nofix:
gnss_state = GnssState.Synchronized gnss_state = GnssState.Synchronized
for ptp4l in self.ptp_monitor_list: for ptp4l in self.ptp_monitor_list:
_, read_state, _ = ptp4l.get_ptp_sync_state() _, read_state, _ = ptp4l.get_ptp_sync_state()
if read_state == PtpState.Holdover or read_state == PtpState.Freerun or \ if read_state == PtpState.Holdover or \
read_state == PtpState.Freerun or \
read_state == constants.UNKNOWN_PHC_STATE: read_state == constants.UNKNOWN_PHC_STATE:
ptp_state = PtpState.Freerun ptp_state = PtpState.Freerun
elif read_state == PtpState.Locked and ptp_state != PtpState.Freerun: elif read_state == PtpState.Locked and \
ptp_state != PtpState.Freerun:
ptp_state = PtpState.Locked ptp_state = PtpState.Locked
os_clock_state = self.os_clock_monitor.get_os_clock_state() os_clock_state = self.os_clock_monitor.get_os_clock_state()
if gnss_state is GnssState.Failure_Nofix or os_clock_state is OsClockState.Freerun or \ if gnss_state is GnssState.Failure_Nofix or \
os_clock_state is OsClockState.Freerun or \
ptp_state is PtpState.Freerun: ptp_state is PtpState.Freerun:
sync_state = OverallClockState.Freerun sync_state = OverallClockState.Freerun
else: else:
sync_state = OverallClockState.Locked sync_state = OverallClockState.Locked
if sync_state == OverallClockState.Freerun: if sync_state == OverallClockState.Freerun:
if previous_sync_state in [constants.UNKNOWN_PHC_STATE, constants.FREERUN_PHC_STATE]: if previous_sync_state in [
constants.UNKNOWN_PHC_STATE,
constants.FREERUN_PHC_STATE]:
sync_state = OverallClockState.Freerun sync_state = OverallClockState.Freerun
elif previous_sync_state == constants.LOCKED_PHC_STATE: elif previous_sync_state == constants.LOCKED_PHC_STATE:
sync_state = OverallClockState.Holdover sync_state = OverallClockState.Holdover
elif previous_sync_state == constants.HOLDOVER_PHC_STATE and \ elif previous_sync_state == constants.HOLDOVER_PHC_STATE and \
time_in_holdover < max_holdover_time: time_in_holdover < max_holdover_time:
LOG.debug("Overall sync: Time in holdover is %s Max time in holdover is %s" % ( LOG.debug("Overall sync: Time in holdover is %s "
time_in_holdover, max_holdover_time)) "Max time in holdover is %s"
% (time_in_holdover, max_holdover_time))
sync_state = OverallClockState.Holdover sync_state = OverallClockState.Holdover
else: else:
sync_state = OverallClockState.Freerun sync_state = OverallClockState.Freerun
@ -450,7 +485,8 @@ class PtpWatcherDefault:
new_event_time = datetime.datetime.utcnow().timestamp() new_event_time = datetime.datetime.utcnow().timestamp()
return new_event, sync_state, new_event_time return new_event, sync_state, new_event_time
def __get_ptp_status(self, holdover_time, freq, sync_state, last_event_time, ptp_monitor): def __get_ptp_status(self, holdover_time, freq, sync_state,
last_event_time, ptp_monitor):
new_event = False new_event = False
new_event_time = last_event_time new_event_time = last_event_time
ptp_monitor.set_ptp_sync_state() ptp_monitor.set_ptp_sync_state()
@ -469,7 +505,8 @@ class PtpWatcherDefault:
else: else:
sync_state = PtpState.Freerun sync_state = PtpState.Freerun
else: else:
new_event, sync_state, new_event_time = ptp_monitor.get_ptp_sync_state() new_event, sync_state, new_event_time = \
ptp_monitor.get_ptp_sync_state()
return new_event, sync_state, new_event_time return new_event, sync_state, new_event_time
'''announce location''' '''announce location'''
@ -478,12 +515,14 @@ class PtpWatcherDefault:
holdover_time = float(self.osclocktracker_context['holdover_seconds']) holdover_time = float(self.osclocktracker_context['holdover_seconds'])
freq = float(self.osclocktracker_context['poll_freq_seconds']) freq = float(self.osclocktracker_context['poll_freq_seconds'])
sync_state = self.osclocktracker_context.get('sync_state', 'Unknown') sync_state = self.osclocktracker_context.get('sync_state', 'Unknown')
last_event_time = self.osclocktracker_context.get('last_event_time', time.time()) last_event_time = self.osclocktracker_context.get('last_event_time',
time.time())
lastStatus = {} lastStatus = {}
new_event, sync_state, new_event_time = self.__get_os_clock_status( new_event, sync_state, new_event_time = self.__get_os_clock_status(
holdover_time, freq, sync_state, last_event_time) holdover_time, freq, sync_state, last_event_time)
LOG.info("os_clock_status: state is %s, new_event is %s " % (sync_state, new_event)) LOG.info("os_clock_status: state is %s, new_event is %s "
% (sync_state, new_event))
if new_event or forced: if new_event or forced:
self.osclocktracker_context_lock.acquire() self.osclocktracker_context_lock.acquire()
self.osclocktracker_context['sync_state'] = sync_state self.osclocktracker_context['sync_state'] = sync_state
@ -512,20 +551,23 @@ class PtpWatcherDefault:
] ]
} }
} }
self.ptpeventproducer.publish_status(lastStatus, constants.SOURCE_SYNC_OS_CLOCK) self.ptpeventproducer.publish_status(
self.ptpeventproducer.publish_status(lastStatus, constants.SOURCE_SYNC_ALL) lastStatus, constants.SOURCE_SYNC_OS_CLOCK)
return self.ptpeventproducer.publish_status(
lastStatus, constants.SOURCE_SYNC_ALL)
def __publish_overall_sync_status(self, forced=False): def __publish_overall_sync_status(self, forced=False):
lastStatus = {} lastStatus = {}
holdover_time = float(self.overalltracker_context['holdover_seconds']) holdover_time = float(self.overalltracker_context['holdover_seconds'])
freq = float(self.overalltracker_context['poll_freq_seconds']) freq = float(self.overalltracker_context['poll_freq_seconds'])
sync_state = self.overalltracker_context.get('sync_state', 'Unknown') sync_state = self.overalltracker_context.get('sync_state', 'Unknown')
last_event_time = self.overalltracker_context.get('last_event_time', time.time()) last_event_time = self.overalltracker_context.get('last_event_time',
time.time())
new_event, sync_state, new_event_time = self.__get_overall_sync_state( new_event, sync_state, new_event_time = self.__get_overall_sync_state(
holdover_time, freq, sync_state, last_event_time) holdover_time, freq, sync_state, last_event_time)
LOG.info("overall_sync_state: state is %s, new_event is %s " % (sync_state, new_event)) LOG.info("overall_sync_state: state is %s, new_event is %s "
% (sync_state, new_event))
if new_event or forced: if new_event or forced:
# Update context # Update context
@ -555,30 +597,36 @@ class PtpWatcherDefault:
] ]
} }
} }
self.ptpeventproducer.publish_status(lastStatus, constants.SOURCE_SYNC_SYNC_STATE) self.ptpeventproducer.publish_status(
self.ptpeventproducer.publish_status(lastStatus, constants.SOURCE_SYNC_ALL) lastStatus, constants.SOURCE_SYNC_SYNC_STATE)
self.ptpeventproducer.publish_status(
lastStatus, constants.SOURCE_SYNC_ALL)
def __publish_gnss_status(self, forced=False): def __publish_gnss_status(self, forced=False):
lastStatus = {} lastStatus = {}
for gnss in self.observer_list: for gnss in self.observer_list:
holdover_time = float( holdover_time = float(
self.gnsstracker_context[gnss.ts2phc_service_name]['holdover_seconds']) self.gnsstracker_context[
freq = float(self.gnsstracker_context[gnss.ts2phc_service_name]['poll_freq_seconds']) gnss.ts2phc_service_name]['holdover_seconds'])
sync_state = self.gnsstracker_context[gnss.ts2phc_service_name].get('sync_state', freq = float(self.gnsstracker_context[
'Unknown') gnss.ts2phc_service_name]['poll_freq_seconds'])
last_event_time = self.gnsstracker_context[gnss.ts2phc_service_name].get( sync_state = \
'last_event_time', self.gnsstracker_context[gnss.ts2phc_service_name].get(
time.time()) 'sync_state', 'Unknown')
last_event_time = \
self.gnsstracker_context[gnss.ts2phc_service_name].get(
'last_event_time', time.time())
new_event, sync_state, new_event_time = self.__get_gnss_status( new_event, sync_state, new_event_time = self.__get_gnss_status(
holdover_time, freq, sync_state, last_event_time, gnss) holdover_time, freq, sync_state, last_event_time, gnss)
LOG.info("%s gnss_status: state is %s, new_event is %s" % ( LOG.info("%s gnss_status: state is %s, new_event is %s"
gnss.ts2phc_service_name, sync_state, new_event)) % (gnss.ts2phc_service_name, sync_state, new_event))
if new_event or forced: if new_event or forced:
# update context # update context
self.gnsstracker_context_lock.acquire() self.gnsstracker_context_lock.acquire()
self.gnsstracker_context[gnss.ts2phc_service_name]['sync_state'] = sync_state self.gnsstracker_context[
gnss.ts2phc_service_name]['sync_state'] = sync_state
self.gnsstracker_context[gnss.ts2phc_service_name][ self.gnsstracker_context[gnss.ts2phc_service_name][
'last_event_time'] = new_event_time 'last_event_time'] = new_event_time
self.gnsstracker_context_lock.release() self.gnsstracker_context_lock.release()
@ -592,7 +640,8 @@ class PtpWatcherDefault:
'id': uuidutils.generate_uuid(), 'id': uuidutils.generate_uuid(),
'specversion': constants.SPEC_VERSION, 'specversion': constants.SPEC_VERSION,
'source': constants.SOURCE_SYNC_GNSS_SYNC_STATUS, 'source': constants.SOURCE_SYNC_GNSS_SYNC_STATUS,
'type': source_type[constants.SOURCE_SYNC_GNSS_SYNC_STATUS], 'type': source_type[
constants.SOURCE_SYNC_GNSS_SYNC_STATUS],
'time': new_event_time, 'time': new_event_time,
'data': { 'data': {
'version': constants.DATA_VERSION, 'version': constants.DATA_VERSION,
@ -606,23 +655,25 @@ class PtpWatcherDefault:
] ]
} }
} }
self.ptpeventproducer.publish_status(lastStatus, self.ptpeventproducer.publish_status(
constants.SOURCE_SYNC_GNSS_SYNC_STATUS) lastStatus, constants.SOURCE_SYNC_GNSS_SYNC_STATUS)
self.ptpeventproducer.publish_status(lastStatus, constants.SOURCE_SYNC_ALL) self.ptpeventproducer.publish_status(
return lastStatus, constants.SOURCE_SYNC_ALL)
def __publish_ptpstatus(self, forced=False): def __publish_ptpstatus(self, forced=False):
lastStatus = {} lastStatus = {}
lastClockClassStatus = {} lastClockClassStatus = {}
for ptp_monitor in self.ptp_monitor_list: for ptp_monitor in self.ptp_monitor_list:
holdover_time = \ holdover_time = float(self.ptptracker_context[
float(self.ptptracker_context[ptp_monitor.ptp4l_service_name]['holdover_seconds']) ptp_monitor.ptp4l_service_name]['holdover_seconds'])
freq = float( freq = float(self.ptptracker_context[
self.ptptracker_context[ptp_monitor.ptp4l_service_name]['poll_freq_seconds']) ptp_monitor.ptp4l_service_name]['poll_freq_seconds'])
sync_state = self.ptptracker_context[ptp_monitor.ptp4l_service_name]. \ sync_state = \
get('sync_state', 'Unknown') self.ptptracker_context[ptp_monitor.ptp4l_service_name].get(
last_event_time = self.ptptracker_context[ptp_monitor.ptp4l_service_name] \ 'sync_state', 'Unknown')
.get('last_event_time', time.time()) last_event_time = \
self.ptptracker_context[ptp_monitor.ptp4l_service_name].get(
'last_event_time', time.time())
new_event, sync_state, new_event_time = self.__get_ptp_status( new_event, sync_state, new_event_time = self.__get_ptp_status(
holdover_time, freq, sync_state, last_event_time, ptp_monitor) holdover_time, freq, sync_state, last_event_time, ptp_monitor)
@ -631,12 +682,14 @@ class PtpWatcherDefault:
new_clock_class_event, clock_class, clock_class_event_time = \ new_clock_class_event, clock_class, clock_class_event_time = \
ptp_monitor.get_ptp_clock_class() ptp_monitor.get_ptp_clock_class()
LOG.info("%s PTP clock class: clockClass is %s, new_event is %s" % ( LOG.info("%s PTP clock class: clockClass is %s, new_event is %s"
ptp_monitor.ptp4l_service_name, clock_class, new_clock_class_event)) % (ptp_monitor.ptp4l_service_name, clock_class,
new_clock_class_event))
if new_event or forced: if new_event or forced:
# update context # update context
self.ptptracker_context_lock.acquire() self.ptptracker_context_lock.acquire()
self.ptptracker_context[ptp_monitor.ptp4l_service_name]['sync_state'] = sync_state self.ptptracker_context[ptp_monitor.ptp4l_service_name][
'sync_state'] = sync_state
self.ptptracker_context[ptp_monitor.ptp4l_service_name][ self.ptptracker_context[ptp_monitor.ptp4l_service_name][
'last_event_time'] = new_event_time 'last_event_time'] = new_event_time
@ -676,19 +729,20 @@ class PtpWatcherDefault:
} }
} }
self.ptptracker_context_lock.release() self.ptptracker_context_lock.release()
self.ptpeventproducer.publish_status(lastStatus, self.ptpeventproducer.publish_status(
constants.SOURCE_SYNC_PTP_LOCK_STATE) lastStatus, constants.SOURCE_SYNC_PTP_LOCK_STATE)
self.ptpeventproducer.publish_status(lastStatus, constants.SOURCE_SYNC_ALL) self.ptpeventproducer.publish_status(
lastStatus, constants.SOURCE_SYNC_ALL)
if new_clock_class_event or forced: if new_clock_class_event or forced:
# update context # update context
self.ptptracker_context_lock.acquire() self.ptptracker_context_lock.acquire()
self.ptptracker_context[ptp_monitor.ptp4l_service_name]['clock_class'] = clock_class self.ptptracker_context[ptp_monitor.ptp4l_service_name][
'clock_class'] = clock_class
self.ptptracker_context[ptp_monitor.ptp4l_service_name][ self.ptptracker_context[ptp_monitor.ptp4l_service_name][
'last_clock_class_event_time'] \ 'last_clock_class_event_time'] \
= clock_class_event_time = clock_class_event_time
resource_address = utils.format_resource_address( resource_address = utils.format_resource_address(
self.node_name, constants.SOURCE_SYNC_PTP_CLOCK_CLASS) self.node_name, constants.SOURCE_SYNC_PTP_CLOCK_CLASS)
@ -711,19 +765,19 @@ class PtpWatcherDefault:
} }
} }
self.ptptracker_context_lock.release() self.ptptracker_context_lock.release()
LOG.info("Publishing clockClass for %s: %s" % (ptp_monitor.ptp4l_service_name, LOG.info("Publishing clockClass for %s: %s"
clock_class)) % (ptp_monitor.ptp4l_service_name, clock_class))
self.ptpeventproducer.publish_status(lastClockClassStatus, self.ptpeventproducer.publish_status(
constants.SOURCE_SYNC_PTP_CLOCK_CLASS) lastClockClassStatus,
constants.SOURCE_SYNC_PTP_CLOCK_CLASS)
self.ptpeventproducer.publish_status(lastClockClassStatus, self.ptpeventproducer.publish_status(lastClockClassStatus,
constants.SOURCE_SYNC_ALL) constants.SOURCE_SYNC_ALL)
return
class DaemonControl(object): class DaemonControl(object):
def __init__(self, sqlalchemy_conf_json, daemon_context_json, process_worker=None): def __init__(self, sqlalchemy_conf_json, daemon_context_json,
process_worker=None):
self.event = mp.Event() self.event = mp.Event()
self.daemon_context = json.loads(daemon_context_json) self.daemon_context = json.loads(daemon_context_json)
self.node_name = self.daemon_context['THIS_NODE_NAME'] self.node_name = self.daemon_context['THIS_NODE_NAME']
@ -733,8 +787,8 @@ class DaemonControl(object):
self.sqlalchemy_conf_json = sqlalchemy_conf_json self.sqlalchemy_conf_json = sqlalchemy_conf_json
self.daemon_context_json = daemon_context_json self.daemon_context_json = daemon_context_json
self.process_worker = process_worker self.process_worker = process_worker
return
def refresh(self): def refresh(self):
self.process_worker(self.event, self.sqlalchemy_conf_json, self.daemon_context_json) self.process_worker(self.event, self.sqlalchemy_conf_json,
self.daemon_context_json)
self.event.set() self.event.set()