[PTP] Support for overall sync status tracking

This commit adds support for tracking the overall time sync status of a
node by looking at the aggregated state of monitored time components and
reporting "Locked/Holdover/Freerun" accordingly. If all subcomponents
are Locked then the overall state reports Locked. If a component is
degraded, then this will cause the overall state to transition.

This functionality allows a consumer to subscribe to a single status
rather than having to handle a subscription for each individual
component.

Also included in this change are some logic fixes for GNSS, PTP and OS
Clock to handle cases where they were not transitioning properly. This
change also replaces the "new_event" variable value with a bool instead
of a string - the new_event var was not working properly previously and
was causing the status to be published even if nothing had changed. This
will reduce the number of updates being sent to clients.

Story: 2010056
Task: 45963

Change-Id: I41fd2bf202df8a9b7d3d1266f50e41a71df78273
Signed-off-by: Cole Walker <cole.walker@windriver.com>
This commit is contained in:
Cole Walker 2022-08-05 15:38:40 -04:00
parent 67452b94db
commit d54873f806
8 changed files with 422 additions and 108 deletions

View File

@ -4,11 +4,14 @@
# SPDX-License-Identifier: Apache-2.0
#
import logging
import datetime
from abc import ABC, abstractmethod
from datetime import datetime
from trackingfunctionsdk.common.helpers import log_helper
from trackingfunctionsdk.common.helpers import constants
from trackingfunctionsdk.common.helpers.cgu_handler import CguHandler
from trackingfunctionsdk.model.dto.gnssstate import GnssState
LOG = logging.getLogger(__name__)
log_helper.config_logger(LOG)
@ -26,7 +29,7 @@ class Observer(ABC):
class GnssMonitor(Observer):
gnss_eec_state = ""
gnss_pps_state = ""
event_time = None
_state = GnssState()
gnss_cgu_handler = None
def __init__(self, config_file, nmea_serialport=None, pci_addr=None, cgu_path=None):
@ -45,7 +48,8 @@ class GnssMonitor(Observer):
self.gnss_cgu_handler.read_cgu()
self.gnss_cgu_handler.cgu_output_to_dict()
self.dmesg_values_to_check = {'pin': 'GNSS-1PPS', 'pci_addr': self.gnss_cgu_handler.pci_addr}
self.dmesg_values_to_check = {'pin': 'GNSS-1PPS',
'pci_addr': self.gnss_cgu_handler.pci_addr}
# Initialize status
if self.gnss_cgu_handler.cgu_output_parsed['EEC DPLL']['Current reference'] == 'GNSS-1PPS':
@ -54,23 +58,50 @@ class GnssMonitor(Observer):
if self.gnss_cgu_handler.cgu_output_parsed['PPS DPLL']['Current reference'] == 'GNSS-1PPS':
self.gnss_pps_state = self.gnss_cgu_handler.cgu_output_parsed['PPS DPLL']['Status']
self.event_time = datetime.now().timestamp()
def update(self, subject, matched_line) -> None:
LOG.info("Kernel event detected. %s" % matched_line)
LOG.debug("GnssMonitor handler logic would run now")
self.set_gnss_status()
def set_gnss_status(self):
self.event_time = datetime.now().timestamp()
self.gnss_cgu_handler.read_cgu()
self.gnss_cgu_handler.cgu_output_to_dict()
self.gnss_eec_state = self.gnss_cgu_handler.cgu_output_parsed['EEC DPLL']['Status']
self.gnss_eec_state = self.gnss_eec_state = \
self.gnss_cgu_handler.cgu_output_parsed['EEC 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 PPS Status is: %s" % self.gnss_pps_state)
if self.gnss_pps_state == 'locked_ho_ack' and self.gnss_eec_state == 'locked_ho_ack':
self._state = GnssState.Locked
else:
self._state = GnssState.Freerun
LOG.debug("Set state GNSS to %s" % self._state)
def get_gnss_status(self, holdover_time, freq, sync_state, event_time):
current_time = datetime.datetime.utcnow().timestamp()
time_in_holdover = round(current_time - event_time)
previous_sync_state = sync_state
max_holdover_time = (holdover_time - freq * 2)
self.set_gnss_status()
if self._state == constants.FREERUN_PHC_STATE:
if previous_sync_state in [constants.UNKNOWN_PHC_STATE, constants.FREERUN_PHC_STATE]:
self._state = constants.FREERUN_PHC_STATE
elif previous_sync_state == constants.LOCKED_PHC_STATE:
self._state = constants.HOLDOVER_PHC_STATE
elif previous_sync_state == constants.HOLDOVER_PHC_STATE and \
time_in_holdover < max_holdover_time:
self._state = constants.HOLDOVER_PHC_STATE
else:
self._state = constants.FREERUN_PHC_STATE
# determine if os clock sync state has changed since the last check
if self._state != previous_sync_state:
new_event = True
event_time = datetime.datetime.utcnow().timestamp()
else:
new_event = False
return new_event, self._state, event_time
def __publish_gnss_status(self, force=False):
LOG.debug("Publish GNSS status.")
# TODO implement a publisher class to handle this
pass

View File

@ -1,18 +1,19 @@
#
# Copyright (c) 2021 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import logging
def get_logger(module_name):
logger = logging.getLogger(module_name)
return config_logger(logger)
def config_logger(logger):
'''
configure the logger: uncomment following lines for debugging
'''
# logger.setLevel(level=logging.DEBUG)
return logger
import logging
import sys
#
# Copyright (c) 2021 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
def get_logger(module_name):
logger = logging.getLogger(module_name)
return config_logger(logger)
def config_logger(logger):
'''
configure the logger: uncomment following lines for debugging
'''
logging.basicConfig(stream=sys.stdout)
logger.setLevel(level=logging.DEBUG)
return logger

View File

@ -27,8 +27,8 @@ class OsClockMonitor:
ptp_device = None
offset = None
def __init__(self, init=True):
self.phc2sys_config = os.environ.get("PHC2SYS_CONFIG", constants.PHC2SYS_DEFAULT_CONFIG)
def __init__(self, init=True, phc2sys_config=constants.PHC2SYS_DEFAULT_CONFIG):
self.phc2sys_config = phc2sys_config
self.set_phc2sys_instance()
"""Normally initialize all fields, but allow these to be skipped to assist with unit testing
@ -124,8 +124,7 @@ class OsClockMonitor:
if offset_int > constants.PHC2SYS_TOLERANCE_HIGH or \
offset_int < constants.PHC2SYS_TOLERANCE_LOW:
LOG.warning("PHC2SYS offset is outside of tolerance, handling state change.")
# TODO Implement handler for os clock state change
pass
self._state = OsClockState.Freerun
else:
LOG.info("PHC2SYS offset is within tolerance, OS clock state is LOCKED")
self._state = OsClockState.Locked
@ -133,6 +132,34 @@ class OsClockMonitor:
def get_os_clock_state(self):
return self._state
def os_clock_status(self, holdover_time, freq, sync_state, event_time):
current_time = datetime.datetime.utcnow().timestamp()
time_in_holdover = round(current_time - event_time)
previous_sync_state = sync_state
max_holdover_time = (holdover_time - freq * 2)
self.get_os_clock_offset()
self.set_os_clock_state()
if self.get_os_clock_state() == constants.FREERUN_PHC_STATE:
if previous_sync_state in [constants.UNKNOWN_PHC_STATE, constants.FREERUN_PHC_STATE]:
self._state = constants.FREERUN_PHC_STATE
elif previous_sync_state == constants.LOCKED_PHC_STATE:
self._state = constants.HOLDOVER_PHC_STATE
elif previous_sync_state == constants.HOLDOVER_PHC_STATE and \
time_in_holdover < max_holdover_time:
self._state = constants.HOLDOVER_PHC_STATE
else:
self._state = constants.FREERUN_PHC_STATE
# determine if os clock sync state has changed since the last check
if self._state != previous_sync_state:
new_event = True
event_time = datetime.datetime.utcnow().timestamp()
else:
new_event = False
return new_event, self.get_os_clock_state(), event_time
if __name__ == "__main__":
# This file can be run in a ptp-notification pod to verify the functionality of

View File

@ -20,8 +20,10 @@ import subprocess
import datetime
import logging
from trackingfunctionsdk.common.helpers import constants
from trackingfunctionsdk.common.helpers import log_helper
LOG = logging.getLogger(__name__)
log_helper.config_logger(LOG)
# dictionary includes PMC commands used and keywords of intrest
ptp_oper_dict = {
@ -166,7 +168,7 @@ def ptp_status(holdover_time, freq, sync_state, event_time):
# Holdover —> Locked #
# Freerun —> Locked #
####################################
current_time = datetime.datetime.now().timestamp()
current_time = datetime.datetime.utcnow().timestamp()
time_in_holdover = round(current_time - event_time)
previous_sync_state = sync_state
# max holdover time is calculated to be in a 'safety' zoon
@ -179,25 +181,28 @@ def ptp_status(holdover_time, freq, sync_state, event_time):
sync_state = check_results(result, total_ptp_keywords, port_count)
else:
sync_state = constants.FREERUN_PHC_STATE
# determine if transition into holdover mode (cannot be in holdover if system clock is not in sync)
# determine if transition into holdover mode (cannot be in holdover if system clock is not in
# sync)
if sync_state == constants.FREERUN_PHC_STATE and phc2sys:
if previous_sync_state in [constants.UNKNOWN_PHC_STATE, constants.FREERUN_PHC_STATE]:
sync_state = constants.FREERUN_PHC_STATE
elif previous_sync_state == constants.LOCKED_PHC_STATE:
sync_state = constants.HOLDOVER_PHC_STATE
elif previous_sync_state == constants.HOLDOVER_PHC_STATE and time_in_holdover < max_holdover_time:
elif previous_sync_state == constants.HOLDOVER_PHC_STATE and time_in_holdover < \
max_holdover_time:
sync_state = constants.HOLDOVER_PHC_STATE
else:
sync_state == constants.FREERUN_PHC_STATE
sync_state = constants.FREERUN_PHC_STATE
# determine if ptp sync state has changed since the last one
if sync_state != previous_sync_state:
new_event = "true"
event_time = datetime.datetime.now().timestamp()
new_event = True
event_time = datetime.datetime.utcnow().timestamp()
else:
new_event = "false"
new_event = False
return new_event, sync_state, event_time
def parse_resource_address(resource_address):
# The format of resource address is:
# /{clusterName}/{siteName}(/optional/hierarchy/..)/{nodeName}/{resource}
@ -207,6 +212,7 @@ def parse_resource_address(resource_address):
resource_path = '/' + re.split('[/]', resource_address, 3)[3]
return clusterName, nodeName, resource_path
def format_resource_address(node_name, resource):
# Return a resource_address
resource_address = '/./' + node_name + resource

View File

@ -0,0 +1,15 @@
#
# Copyright (c) 2022 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from wsme import types as wtypes
EnumOverallClockState = wtypes.Enum(str, 'Locked', 'Freerun', 'Holdover')
class OverallClockState(object):
Locked = "Locked"
Freerun = "Freerun"
Holdover = "Holdover"

View File

@ -1,20 +1,17 @@
#
# Copyright (c) 2022 Wind River Systems, Inc.
# Copyright (c) 2021-2022 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import os
import datetime
import json
import time
import oslo_messaging
from oslo_config import cfg
from oslo_utils import uuidutils
import logging
import multiprocessing as mp
import os
import sys
import threading
import time
from oslo_utils import uuidutils
from trackingfunctionsdk.client.ptpeventproducer import PtpEventProducer
from trackingfunctionsdk.common.helpers import constants
@ -22,22 +19,18 @@ from trackingfunctionsdk.common.helpers import ptpsync
from trackingfunctionsdk.common.helpers import log_helper
from trackingfunctionsdk.common.helpers.dmesg_watcher import DmesgWatcher
from trackingfunctionsdk.common.helpers.gnss_monitor import GnssMonitor
from trackingfunctionsdk.common.helpers.os_clock_monitor import OsClockMonitor
from trackingfunctionsdk.model.dto.ptpstate import PtpState
from trackingfunctionsdk.model.dto.gnssstate import GnssState
from trackingfunctionsdk.model.dto.osclockstate import OsClockState
from trackingfunctionsdk.model.dto.overallclockstate import OverallClockState
from trackingfunctionsdk.model.dto.resourcetype import ResourceType
from trackingfunctionsdk.model.dto.rpc_endpoint import RpcEndpointInfo
from trackingfunctionsdk.model.dto.resourcetype import ResourceType
from trackingfunctionsdk.model.dto.ptpstate import PtpState
from trackingfunctionsdk.client.ptpeventproducer import PtpEventProducer
from trackingfunctionsdk.common.helpers import ptpsync as ptpsync
LOG = logging.getLogger(__name__)
from trackingfunctionsdk.common.helpers import log_helper
log_helper.config_logger(LOG)
THIS_NODE_NAME = os.environ.get("THIS_NODE_NAME",'controller-0')
THIS_NODE_NAME = os.environ.get("THIS_NODE_NAME", 'controller-0')
# Event source to event type mapping
source_type = {
@ -53,6 +46,8 @@ source_type = {
}
'''Entry point of Default Process Worker'''
def ProcessWorkerDefault(event, sqlalchemy_conf_json, broker_transport_endpoint):
worker = PtpWatcherDefault(event, sqlalchemy_conf_json, broker_transport_endpoint)
worker.run()
@ -65,6 +60,21 @@ class PtpWatcherDefault:
'poll_freq_seconds': 2
}
DEFAULT_GNSSTRACKER_CONTEXT = {
'holdover_seconds': 30,
'poll_freq_seconds': 2
}
DEFAULT_OS_CLOCK_TRACKER_CONTEXT = {
'holdover_seconds': 30,
'poll_freq_seconds': 2
}
DEFAULT_OVERALL_SYNC_TRACKER_CONTEXT = {
'holdover_seconds': 30,
'poll_freq_seconds': 2
}
class PtpRequestHandlerDefault(object):
def __init__(self, watcher):
self.watcher = watcher
@ -127,8 +137,26 @@ class PtpWatcherDefault:
self.ptptracker_context['last_event_time'] = self.init_time
self.ptptracker_context_lock = threading.Lock()
self.gnsstracker_context = self.daemon_context.get(
'gnsstracker_context', PtpWatcherDefault.DEFAULT_GNSSTRACKER_CONTEXT)
self.gnsstracker_context['sync_state'] = GnssState.Freerun
self.gnsstracker_context['last_event_time'] = self.init_time
self.gnsstracker_context_lock = threading.Lock()
self.osclocktracker_context = self.daemon_context.get(
'os_clock_tracker_context', PtpWatcherDefault.DEFAULT_OS_CLOCK_TRACKER_CONTEXT)
self.osclocktracker_context['sync_state'] = OsClockState.Freerun
self.osclocktracker_context['last_event_time'] = self.init_time
self.osclocktracker_context_lock = threading.Lock()
self.overalltracker_context = self.daemon_context.get(
'overall_sync_tracker_context', PtpWatcherDefault.DEFAULT_OVERALL_SYNC_TRACKER_CONTEXT)
self.overalltracker_context['sync_state'] = OverallClockState.Freerun
self.overalltracker_context['last_event_time'] = self.init_time
self.overalltracker_context_lock = threading.Lock()
self.ptp_device_simulated = "true" == self.ptptracker_context.get('device_simulated',
"False").lower()
"False")
self.event_timeout = float(self.ptptracker_context['poll_freq_seconds'])
@ -151,12 +179,15 @@ class PtpWatcherDefault:
self.forced_publishing = False
self.watcher = DmesgWatcher()
observer_list = [GnssMonitor(i) for i in self.daemon_context['GNSS_CONFIGS']]
for observer in observer_list:
self.observer_list = [GnssMonitor(i) for i in self.daemon_context['GNSS_CONFIGS']]
for observer in self.observer_list:
self.watcher.attach(observer)
self.watcher_thread = threading.Thread(target=self.watcher.run_watcher)
# Setup OS Clock monitor
self.os_clock_monitor = OsClockMonitor(phc2sys_config=self.daemon_context['PHC2SYS_CONFIG'])
def signal_ptp_event(self):
if self.event:
self.event.set()
@ -167,13 +198,18 @@ class PtpWatcherDefault:
def run(self):
# start location listener
self.__start_listener()
# Start dmesg watcher
# start GNSS monitoring
self.watcher_thread.start()
while True:
# announce the location
forced = self.forced_publishing
self.forced_publishing = False
self.__publish_ptpstatus(forced)
self.__publish_os_clock_status(forced)
self.__publish_gnss_status(forced)
self.__publish_overall_sync_status(forced)
if self.event.wait(self.event_timeout):
LOG.debug("daemon control event is asserted")
self.event.clear()
@ -184,12 +220,13 @@ class PtpWatcherDefault:
self.__stop_listener()
'''Start listener to answer querying from clients'''
def __start_listener(self):
LOG.debug("start listener to answer location querying")
self.ptpeventproducer.start_status_listener(
self.__ptprequest_handler
)
)
return
def __stop_listener(self):
@ -198,6 +235,62 @@ class PtpWatcherDefault:
self.ptpeventproducer.stop_status_listener(self.location_info)
return
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(
holdover_time, freq, sync_state, last_event_time)
LOG.debug("Getting GNSS status.")
return new_event, sync_state, new_event_time
def __get_os_clock_status(self, 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.")
return new_event, sync_state, new_event_time
def __get_overall_sync_state(self, holdover_time, freq, sync_state, last_event_time):
new_event = False
new_event_time = last_event_time
previous_sync_state = sync_state
current_time = datetime.datetime.utcnow().timestamp()
time_in_holdover = round(current_time - last_event_time)
max_holdover_time = (holdover_time - freq * 2)
gnss_state = None
os_clock_state = None
ptp_state = None
LOG.debug("Getting overall sync state.")
for gnss in self.observer_list:
if gnss._state == GnssState.Holdover or gnss._state == GnssState.Freerun:
gnss_state = GnssState.Freerun
elif gnss._state == GnssState.Locked and gnss_state != GnssState.Freerun:
gnss_state = GnssState.Locked
os_clock_state = self.os_clock_monitor.get_os_clock_state()
ptp_state = self.ptptracker_context.get('sync_state')
if gnss_state is GnssState.Freerun or os_clock_state is OsClockState.Freerun or ptp_state\
is PtpState.Freerun:
sync_state = OverallClockState.Freerun
else:
sync_state = OverallClockState.Locked
if sync_state == OverallClockState.Freerun:
if previous_sync_state in [constants.UNKNOWN_PHC_STATE, constants.FREERUN_PHC_STATE]:
sync_state = OverallClockState.Freerun
elif previous_sync_state == constants.LOCKED_PHC_STATE:
sync_state = OverallClockState.Holdover
elif previous_sync_state == constants.HOLDOVER_PHC_STATE and \
time_in_holdover < max_holdover_time:
sync_state = OverallClockState.Holdover
else:
sync_state = OverallClockState.Freerun
if sync_state != previous_sync_state:
new_event = True
new_event_time = datetime.datetime.utcnow().timestamp()
return new_event, sync_state, new_event_time
def __get_ptp_status(self, holdover_time, freq, sync_state, last_event_time):
new_event = False
new_event_time = last_event_time
@ -220,27 +313,27 @@ class PtpWatcherDefault:
holdover_time, freq, sync_state, last_event_time)
return new_event, sync_state, new_event_time
'''publish ptp status'''
def __publish_ptpstatus(self, forced=False):
holdover_time = float(self.ptptracker_context['holdover_seconds'])
freq = float(self.ptptracker_context['poll_freq_seconds'])
sync_state = self.ptptracker_context.get('sync_state', 'Unknown')
last_event_time = self.ptptracker_context.get('last_event_time', time.time())
'''announce location'''
new_event, sync_state, new_event_time = self.__get_ptp_status(
def __publish_os_clock_status(self, forced=False):
holdover_time = float(self.osclocktracker_context['holdover_seconds'])
freq = float(self.osclocktracker_context['poll_freq_seconds'])
sync_state = self.osclocktracker_context.get('sync_state', 'Unknown')
last_event_time = self.osclocktracker_context.get('last_event_time', time.time())
new_event, sync_state, new_event_time = self.__get_os_clock_status(
holdover_time, freq, sync_state, last_event_time)
LOG.debug("Got os clock status.")
if new_event or forced:
# update context
self.ptptracker_context_lock.acquire()
self.ptptracker_context['sync_state'] = sync_state
self.ptptracker_context['last_event_time'] = new_event_time
self.ptptracker_context_lock.release()
self.osclocktracker_context_lock.acquire()
self.osclocktracker_context['sync_state'] = sync_state
self.osclocktracker_context['last_event_time'] = new_event_time
self.osclocktracker_context_lock.release()
# publish new event in API version v1 format
LOG.debug("publish ptp status to clients")
LOG.debug("Publish OS Clock Status")
lastStatus = {
'ResourceType': 'PTP',
'ResourceType': 'OS Clock',
'EventData': {
'State': sync_state
},
@ -249,11 +342,50 @@ class PtpWatcherDefault:
},
'EventTimestamp': new_event_time
}
self.ptpeventproducer.publish_status(lastStatus, 'PTP')
# publish new event in API version v2 format
resource_address = ptpsync.format_resource_address(
self.node_name, constants.SOURCE_SYNC_SYNC_STATE)
self.node_name, constants.SOURCE_SYNC_OS_CLOCK)
lastStatus = {
'id': uuidutils.generate_uuid(),
'specversion': constants.SPEC_VERSION,
'source': constants.SOURCE_SYNC_OS_CLOCK,
'type': source_type[constants.SOURCE_SYNC_OS_CLOCK],
'time': new_event_time,
'data': {
'version': constants.DATA_VERSION,
'values': [
{
'data_type': constants.DATA_TYPE_NOTIFICATION,
'ResourceAddress': resource_address,
'value_type': constants.VALUE_TYPE_ENUMERATION,
'value': sync_state
}
]
}
}
self.ptpeventproducer.publish_status(lastStatus, constants.SOURCE_SYNC_OS_CLOCK)
self.ptpeventproducer.publish_status(lastStatus, constants.SOURCE_SYNC_ALL)
return
def __publish_overall_sync_status(self, forced=False):
holdover_time = float(self.overalltracker_context['holdover_seconds'])
freq = float(self.overalltracker_context['poll_freq_seconds'])
sync_state = self.overalltracker_context.get('sync_state', 'Unknown')
last_event_time = self.overalltracker_context.get('last_event_time', time.time())
new_event, sync_state, new_event_time = self.__get_overall_sync_state(
holdover_time, freq, sync_state, last_event_time)
if new_event or forced:
# Update context
self.overalltracker_context_lock.acquire()
self.overalltracker_context['sync_state'] = sync_state
self.overalltracker_context['last_event_time'] = new_event_time
self.overalltracker_context_lock.release()
LOG.debug("Publish overall sync status.")
resource_address = ptpsync.format_resource_address(
self.node_name, constants.SOURCE_SYNC_SYNC_STATE)
lastStatus = {
'id': uuidutils.generate_uuid(),
'specversion': constants.SPEC_VERSION,
@ -272,14 +404,115 @@ class PtpWatcherDefault:
]
}
}
self.ptpeventproducer.publish_status(lastStatus, constants.SOURCE_SYNC_SYNC_STATE)
self.ptpeventproducer.publish_status(lastStatus, constants.SOURCE_SYNC_ALL)
def __publish_gnss_status(self, forced=False):
holdover_time = float(self.gnsstracker_context['holdover_seconds'])
freq = float(self.gnsstracker_context['poll_freq_seconds'])
sync_state = self.gnsstracker_context.get('sync_state', 'Unknown')
last_event_time = self.gnsstracker_context.get('last_event_time', time.time())
LOG.debug("GNSS sync_state %s" % sync_state)
for gnss in self.observer_list:
new_event, sync_state, new_event_time = self.__get_gnss_status(
holdover_time, freq, sync_state, last_event_time, gnss)
if new_event or forced:
# update context
self.gnsstracker_context_lock.acquire()
self.gnsstracker_context['sync_state'] = sync_state
self.gnsstracker_context['last_event_time'] = new_event_time
self.gnsstracker_context_lock.release()
LOG.debug("Publish GNSS status.")
# publish new event in API version v2 format
resource_address = ptpsync.format_resource_address(
self.node_name, constants.SOURCE_SYNC_GNSS_SYNC_STATUS)
lastStatus = {
'id': uuidutils.generate_uuid(),
'specversion': constants.SPEC_VERSION,
'source': constants.SOURCE_SYNC_GNSS_SYNC_STATUS,
'type': source_type[constants.SOURCE_SYNC_GNSS_SYNC_STATUS],
'time': new_event_time,
'data': {
'version': constants.DATA_VERSION,
'values': [
{
'data_type': constants.DATA_TYPE_NOTIFICATION,
'ResourceAddress': resource_address,
'value_type': constants.VALUE_TYPE_ENUMERATION,
'value': sync_state
}
]
}
}
self.ptpeventproducer.publish_status(lastStatus,
constants.SOURCE_SYNC_GNSS_SYNC_STATUS)
self.ptpeventproducer.publish_status(lastStatus, constants.SOURCE_SYNC_ALL)
return
def __publish_ptpstatus(self, forced=False):
holdover_time = float(self.ptptracker_context['holdover_seconds'])
freq = float(self.ptptracker_context['poll_freq_seconds'])
sync_state = self.ptptracker_context.get('sync_state', 'Unknown')
last_event_time = self.ptptracker_context.get('last_event_time', time.time())
new_event, sync_state, new_event_time = self.__get_ptp_status(
holdover_time, freq, sync_state, last_event_time)
if new_event or forced:
# update context
self.ptptracker_context_lock.acquire()
self.ptptracker_context['sync_state'] = sync_state
self.ptptracker_context['last_event_time'] = new_event_time
self.ptptracker_context_lock.release()
# publish new event
LOG.debug("Publish ptp status to clients")
lastStatus = {
'ResourceType': 'PTP',
'EventData': {
'State': sync_state
},
'ResourceQualifier': {
'NodeName': self.node_name
},
'EventTimestamp': new_event_time
}
self.ptpeventproducer.publish_status(lastStatus, 'PTP')
# publish new event in API version v2 format
resource_address = ptpsync.format_resource_address(
self.node_name, constants.SOURCE_SYNC_PTP_LOCK_STATE)
lastStatus = {
'id': uuidutils.generate_uuid(),
'specversion': constants.SPEC_VERSION,
'source': constants.SOURCE_SYNC_PTP_LOCK_STATE,
'type': source_type[constants.SOURCE_SYNC_PTP_LOCK_STATE],
'time': new_event_time,
'data': {
'version': constants.DATA_VERSION,
'values': [
{
'data_type': constants.DATA_TYPE_NOTIFICATION,
'ResourceAddress': resource_address,
'value_type': constants.VALUE_TYPE_ENUMERATION,
'value': sync_state
}
]
}
}
self.ptpeventproducer.publish_status(lastStatus, constants.SOURCE_SYNC_PTP_LOCK_STATE)
self.ptpeventproducer.publish_status(lastStatus, constants.SOURCE_SYNC_ALL)
return
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.daemon_context = json.loads(daemon_context_json)
self.node_name = self.daemon_context['THIS_NODE_NAME']

View File

@ -14,13 +14,13 @@ from trackingfunctionsdk.common.helpers.os_clock_monitor import OsClockMonitor
from trackingfunctionsdk.model.dto.osclockstate import OsClockState
testpath = os.environ.get("TESTPATH", "")
phc2sys_test_config = constants.PTP_CONFIG_PATH + "phc2sys-phc2sys-test.conf"
class OsClockMonitorTests(unittest.TestCase):
os.environ["PHC2SYS_CONFIG"] = constants.PTP_CONFIG_PATH + "phc2sys-phc2sys-test.conf"
clockmon = OsClockMonitor(init=False)
clockmon = OsClockMonitor(init=False, phc2sys_config=phc2sys_test_config)
def test_set_phc2sys_instance(self):
self.clockmon = OsClockMonitor(init=False)
self.clockmon = OsClockMonitor(init=False, phc2sys_config=phc2sys_test_config)
self.clockmon.set_phc2sys_instance()
assert self.clockmon.phc2sys_instance == "phc2sys-test"

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2021 Wind River Systems, Inc.
# Copyright (c) 2021-2022 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -28,46 +28,47 @@ log_helper.config_logger(LOG)
import os
import json
import time # 引入time模块
import oslo_messaging
from oslo_config import cfg
from trackingfunctionsdk.services.daemon import DaemonControl
THIS_NAMESPACE = os.environ.get("THIS_NAMESPACE",'notification')
THIS_NODE_NAME = os.environ.get("THIS_NODE_NAME",'controller-1')
THIS_POD_IP = os.environ.get("THIS_POD_IP",'127.0.0.1')
REGISTRATION_USER = os.environ.get("REGISTRATION_USER", "admin")
REGISTRATION_PASS = os.environ.get("REGISTRATION_PASS", "admin")
THIS_NAMESPACE = os.environ.get("THIS_NAMESPACE", 'notification')
THIS_NODE_NAME = os.environ.get("THIS_NODE_NAME", 'controller-0')
THIS_POD_IP = os.environ.get("THIS_POD_IP", '127.0.0.1')
REGISTRATION_USER = os.environ.get("REGISTRATION_USER", "guest")
REGISTRATION_PASS = os.environ.get("REGISTRATION_PASS", "guest")
REGISTRATION_PORT = os.environ.get("REGISTRATION_PORT", "5672")
REGISTRATION_HOST = os.environ.get("REGISTRATION_HOST",'registration.notification.svc.cluster.local')
# REGISTRATION_HOST = os.environ.get("REGISTRATION_HOST", 'registration.notification.svc.cluster.local')
REGISTRATION_HOST = os.environ.get("REGISTRATION_HOST", 'localhost')
# 'rabbit://admin:admin@[127.0.0.1]:5672/'
# 'rabbit://admin:admin@[::1]:5672/'
REGISTRATION_TRANSPORT_ENDPOINT = 'rabbit://{0}:{1}@[{2}]:{3}'.format(
REGISTRATION_USER, REGISTRATION_PASS, REGISTRATION_HOST, REGISTRATION_PORT)
REGISTRATION_USER, REGISTRATION_PASS, REGISTRATION_HOST, REGISTRATION_PORT)
NOTIFICATION_BROKER_USER = os.environ.get("NOTIFICATIONSERVICE_USER", "admin")
NOTIFICATION_BROKER_PASS = os.environ.get("NOTIFICATIONSERVICE_PASS", "admin")
NOTIFICATION_BROKER_USER = os.environ.get("NOTIFICATIONSERVICE_USER", "guest")
NOTIFICATION_BROKER_PASS = os.environ.get("NOTIFICATIONSERVICE_PASS", "guest")
NOTIFICATION_BROKER_PORT = os.environ.get("NOTIFICATIONSERVICE_PORT", "5672")
NOTIFICATION_TRANSPORT_ENDPOINT = 'rabbit://{0}:{1}@[{2}]:{3}'.format(
NOTIFICATION_BROKER_USER, NOTIFICATION_BROKER_PASS, THIS_POD_IP, NOTIFICATION_BROKER_PORT)
NOTIFICATION_BROKER_USER, NOTIFICATION_BROKER_PASS, THIS_POD_IP, NOTIFICATION_BROKER_PORT)
PTP_DEVICE_SIMULATED = os.environ.get("PTP_DEVICE_SIMULATED", False)
PTP_DEVICE_SIMULATED = os.environ.get("PTP_DEVICE_SIMULATED", True)
PTP_HOLDOVER_SECONDS = os.environ.get("PTP_HOLDOVER_SECONDS", 30)
PTP_POLL_FREQ_SECONDS = os.environ.get("PTP_POLL_FREQ_SECONDS", 2)
GNSS_CONFIGS = os.environ.get("TS2PHC_CONFIGS", ["/ptp/ptpinstance/ts2phc-ts1.conf"])
PHC2SYS_CONFIG = os.environ.get("PHC2SYS_CONFIG", "/ptp/ptpinstance/phc2sys-phc-inst1.conf")
context = {
'THIS_NAMESPACE': THIS_NAMESPACE,
'THIS_NODE_NAME': THIS_NODE_NAME,
'THIS_POD_IP': THIS_POD_IP,
'REGISTRATION_TRANSPORT_ENDPOINT': REGISTRATION_TRANSPORT_ENDPOINT,
'NOTIFICATION_TRANSPORT_ENDPOINT': NOTIFICATION_TRANSPORT_ENDPOINT,
# 'NOTIFICATION_BROKER_USER': NOTIFICATION_BROKER_USER,
# 'NOTIFICATION_BROKER_PASS': NOTIFICATION_BROKER_PASS,
# 'NOTIFICATION_BROKER_PORT': NOTIFICATION_BROKER_PORT
'GNSS_CONFIGS': GNSS_CONFIGS,
'PHC2SYS_CONFIG': PHC2SYS_CONFIG,
'ptptracker_context': {
'device_simulated': PTP_DEVICE_SIMULATED,
'holdover_seconds': PTP_HOLDOVER_SECONDS,
@ -76,11 +77,11 @@ context = {
}
sqlalchemy_conf = {
'url' : 'sqlite:///apiserver.db',
'echo' : False,
'echo_pool' : False,
'pool_recycle' : 3600,
'encoding' : 'utf-8'
'url': 'sqlite:///apiserver.db',
'echo': False,
'echo_pool': False,
'pool_recycle': 3600,
'encoding': 'utf-8'
}
sqlalchemy_conf_json = json.dumps(sqlalchemy_conf)