| # |
| # Copyright 2017 the original author or authors. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| from scapy.automaton import ATMT |
| import structlog |
| from voltha.adapters.microsemi_olt.BaseOltAutomaton import BaseOltAutomaton |
| from voltha.adapters.microsemi_olt.PAS5211 import PAS5211MsgGetProtocolVersion, PAS5211MsgGetOltVersion, \ |
| PAS5211MsgGetOltVersionResponse, PAS5211MsgGetProtocolVersionResponse, \ |
| SnrBurstDelay, RngBurstDelay, GeneralOpticsParams, ResetValues, ResetTimingCtrl, PreambleParams, \ |
| PAS5211MsgSetOltOpticsResponse, CHANNELS, PAS5211MsgSetOpticsIoControlResponse, PAS5211MsgGetGeneralParamResponse, \ |
| PAS5211MsgAddOltChannel, \ |
| PAS5211MsgAddOltChannelResponse, PAS5211MsgSetAlarmConfigResponse, PAS5211MsgGetDbaMode, \ |
| PAS5211MsgGetDbaModeResponse, PAS5211MsgStartDbaAlgorithm, \ |
| PAS5211MsgStartDbaAlgorithmResponse, PAS5211MsgSetOltChannelActivationPeriod, \ |
| PAS5211MsgSetOltChannelActivationPeriodResponse, PAS5211MsgHeader, PAS5211Event, PAS5211EventAlarmNotification |
| from voltha.adapters.microsemi_olt.PAS5211_constants import PON_OPTICS_VOLTAGE_IF_LVPECL, PON_ENABLE, \ |
| PON_POLARITY_ACTIVE_HIGH, PASCOMM_RETRIES, \ |
| PON_SD_SOURCE_LASER_SD, PON_RESET_TYPE_DELAY_BASED, PON_DISABLE, PON_RESET_TYPE_NORMAL_START_BURST_BASED, \ |
| PON_TX_ENABLE_DEFAULT, PON_ALARM_LOS, PON_DBA_MODE_LOADED_NOT_RUNNING, PON_DBA_MODE_RUNNING |
| from voltha.adapters.microsemi_olt.PAS5211_utils import general_param, olt_optics_pkt, burst_timing, io_ctrl_optics, \ |
| alarm_config |
| from voltha.extensions.omci.omci_frame import OmciFrame |
| import structlog |
| |
| from voltha.protos.common_pb2 import ConnectStatus |
| |
| log = structlog.get_logger() |
| |
| |
| class OltStateMachine(BaseOltAutomaton): |
| send_state = [] |
| dba_needs_start = False |
| retry = 10 |
| |
| def check_channel_state(self): |
| for i in CHANNELS: |
| if not self.send_state[i]: |
| return False |
| self.send_state = [] |
| return True |
| |
| def master_filter(self, pkt): |
| |
| if not super(OltStateMachine, self).master_filter(pkt): |
| return False |
| |
| if OmciFrame in pkt: |
| log.debug("OMCI-message-type: {}".format(pkt[OmciFrame].message_type)) |
| return False |
| |
| if PAS5211Event in pkt: |
| return False |
| |
| if PAS5211EventAlarmNotification in pkt: |
| return False |
| |
| return True |
| |
| """ |
| States |
| """ |
| |
| @ATMT.state(initial=1) |
| def disconnected(self): |
| pass |
| |
| @ATMT.state() |
| def wait_for_proto_version(self): |
| pass |
| |
| @ATMT.state() |
| def got_proto_version(self): |
| pass |
| |
| @ATMT.state() |
| def wait_for_olt_version(self): |
| pass |
| |
| @ATMT.state() |
| def got_olt_version(self): |
| pass |
| |
| @ATMT.state() |
| def wait_olt_optics(self): |
| pass |
| |
| @ATMT.state() |
| def got_olt_optics(self): |
| pass |
| |
| @ATMT.state() |
| def wait_olt_io_optics(self): |
| pass |
| |
| @ATMT.state() |
| def got_olt_io_optics(self): |
| pass |
| |
| @ATMT.state() |
| def wait_query_response(self): |
| pass |
| |
| @ATMT.state() |
| def got_query_response(self): |
| pass |
| |
| @ATMT.state() |
| def wait_olt_add(self): |
| pass |
| |
| @ATMT.state() |
| def got_olt_add(self): |
| pass |
| |
| @ATMT.state() |
| def wait_alarm_set(self): |
| pass |
| |
| @ATMT.state() |
| def got_alarm_set(self): |
| pass |
| |
| @ATMT.state() |
| def wait_dba_mode(self): |
| pass |
| |
| @ATMT.state() |
| def got_dba_mode(self): |
| pass |
| |
| @ATMT.state() |
| def wait_dba_start(self): |
| pass |
| |
| @ATMT.state() |
| def got_dba_start(self): |
| pass |
| |
| @ATMT.state() |
| def wait_activation(self): |
| pass |
| |
| @ATMT.state(final=1) |
| def end(self): |
| log.debug('OLT state machine ended') |
| # |
| # @ATMT.state() |
| # def wait_keepalive(self): |
| # pass |
| |
| @ATMT.state(error=1) |
| def error(self): |
| raise self.end() |
| # pass |
| |
| """ |
| Transitions |
| """ |
| |
| # Transitions from disconnected state |
| @ATMT.condition(disconnected) |
| def send_proto_request(self): |
| self.send(self.p(PAS5211MsgGetProtocolVersion())) |
| raise self.wait_for_proto_version() |
| |
| # Transitions from wait_for_proto_version |
| @ATMT.timeout(wait_for_proto_version, 3) |
| def timeout_proto(self): |
| log.debug("Timed out waiting for proto version") |
| self.retry -= 1 |
| if self.retry < 0: |
| log.error("Too many retries, aborting.") |
| raise self.error() |
| raise self.disconnected() |
| |
| @ATMT.receive_condition(wait_for_proto_version) |
| def receive_proto_version(self, pkt): |
| log.debug("Received proto version {}".format(type(pkt))) |
| if PAS5211MsgGetProtocolVersionResponse in pkt: |
| raise self.got_proto_version() |
| else: |
| log.error("Got garbage packet: {}".format(pkt.summary())) |
| self.retry -= 1 |
| if self.retry < 0: |
| log.error("Too many retries, aborting.") |
| raise self.error() |
| else: |
| raise self.wait_for_proto_version() |
| |
| # Transitions from got_proto_version |
| @ATMT.condition(got_proto_version) |
| def send_olt_version(self): |
| self.send(self.p(PAS5211MsgGetOltVersion())) |
| log.debug("[SENT] PAS5211MsgGetOltVersion") |
| raise self.wait_for_olt_version() |
| |
| # Transitions from waiting for olt version |
| @ATMT.timeout(wait_for_olt_version, 3) |
| def timeout_olt(self): |
| log.debug("Timed out waiting for olt version") |
| self.retry -= 1 |
| if self.retry < 0: |
| log.error("Too many retries, aborting.") |
| raise self.error() |
| raise self.disconnected() |
| |
| @ATMT.receive_condition(wait_for_olt_version) |
| def receive_olt_version(self, pkt): |
| log.debug("Received proto version") |
| if PAS5211MsgGetOltVersionResponse in pkt: |
| log.debug("[RESPONSE] PAS5211MsgGetOltVersion") |
| self.device.update_device_info_from_pkt(pkt) |
| raise self.got_olt_version() |
| else: |
| log.error("Got garbage packet: {}".format(pkt.summary())) |
| raise self.error() |
| |
| # Transitions from got_olt_version |
| @ATMT.condition(got_olt_version) |
| def send_olt_optics(self): |
| snr_burst_delay = SnrBurstDelay(timer_delay=8, preamble_delay=32, |
| delimiter_delay=128, burst_delay=128) |
| rng_burst_delay = RngBurstDelay(timer_delay=8, preamble_delay=32, |
| delimiter_delay=128) |
| |
| general_optics_param = GeneralOpticsParams(laser_reset_polarity=PON_POLARITY_ACTIVE_HIGH, |
| laser_sd_polarity=PON_POLARITY_ACTIVE_HIGH, |
| sd_source=PON_SD_SOURCE_LASER_SD, sd_hold_snr_ranging=PON_DISABLE, |
| sd_hold_normal=PON_DISABLE, |
| reset_type_snr_ranging=PON_RESET_TYPE_DELAY_BASED, |
| reset_type_normal=PON_RESET_TYPE_NORMAL_START_BURST_BASED, |
| laser_reset_enable=PON_ENABLE) |
| |
| reset = ResetTimingCtrl(reset_data_burst=ResetValues(bcdr_reset_d2=1, |
| bcdr_reset_d1=11, |
| laser_reset_d2=2, |
| laser_reset_d1=5), |
| reset_snr_burst=ResetValues(bcdr_reset_d2=2, |
| bcdr_reset_d1=9, |
| laser_reset_d2=2, |
| laser_reset_d1=1), |
| reset_rng_burst=ResetValues(bcdr_reset_d2=2, |
| bcdr_reset_d1=9, |
| laser_reset_d2=2, |
| laser_reset_d1=1), |
| single_reset=ResetValues(bcdr_reset_d2=1, |
| bcdr_reset_d1=1, |
| laser_reset_d2=1, |
| laser_reset_d1=1), |
| double_reset=ResetValues(bcdr_reset_d2=1, |
| bcdr_reset_d1=1, |
| laser_reset_d2=1, |
| laser_reset_d1=1)) |
| |
| preamble = PreambleParams(correlation_preamble_length=8, preamble_length_snr_rng=119, |
| guard_time_data_mode=32, type1_size_data=0, |
| type2_size_data=0, type3_size_data=5, |
| type3_pattern=170, delimiter_size=20, |
| delimiter_byte1=171, delimiter_byte2=89, |
| delimiter_byte3=131) |
| |
| olt_optics = olt_optics_pkt(PON_OPTICS_VOLTAGE_IF_LVPECL, burst=burst_timing(1, 1, |
| snr_burst=snr_burst_delay, |
| rng_burst=rng_burst_delay), |
| general=general_optics_param, |
| reset=reset, |
| preamble=preamble) |
| |
| for id in CHANNELS: |
| self.send_state.append(False) |
| self.send(self.p(olt_optics, channel_id=id)) |
| log.debug("[SENT] PAS5211MsgSetOltOptics") |
| |
| raise self.wait_olt_optics() |
| |
| # Transitions from wait_olt_optics |
| @ATMT.timeout(wait_olt_optics, 3) |
| def olt_optics_timeout(self): |
| log.error("Setting olt optics failed; disconnecting") |
| raise self.error() |
| |
| @ATMT.receive_condition(wait_olt_optics) |
| def receive_set_optics_response(self, pkt): |
| if PAS5211MsgSetOltOpticsResponse in pkt: |
| log.debug("[RESPONSE] PAS5211MsgSetOltOptics") |
| self.send_state[pkt.channel_id] = True |
| if self.check_channel_state(): |
| raise self.got_olt_optics() |
| raise self.wait_olt_optics() |
| |
| # Transitions from got_olt_optics |
| @ATMT.condition(got_olt_optics) |
| def send_olt_io_optics(self): |
| |
| pkt = io_ctrl_optics(1, 0, 6, 14) |
| self.send_state.append(False) |
| self.send(self.p(pkt, channel_id=0)) |
| log.debug("[SENT] PAS5211MsgSetOpticsIoControl") |
| |
| pkt = io_ctrl_optics(3, 2, 7, 15) |
| self.send_state.append(False) |
| self.send(self.p(pkt, channel_id=1)) |
| log.debug("[SENT] PAS5211MsgSetOpticsIoControl") |
| |
| pkt = io_ctrl_optics(11, 10, 8, 16) |
| self.send_state.append(False) |
| self.send(self.p(pkt, channel_id=2)) |
| log.debug("[SENT] PAS5211MsgSetOpticsIoControl") |
| |
| pkt = io_ctrl_optics(13, 12, 9, 17) |
| self.send_state.append(False) |
| self.send(self.p(pkt, channel_id=3)) |
| log.debug("[SENT] PAS5211MsgSetOpticsIoControl") |
| |
| raise self.wait_olt_io_optics() |
| |
| # Transitions from wait olt io optics |
| @ATMT.timeout(wait_olt_io_optics, 3) |
| def olt_io_optics_timeout(self): |
| log.error("Setting olt io optics failed; disconnecting") |
| raise self.error() |
| |
| @ATMT.receive_condition(wait_olt_io_optics) |
| def receive_io_optics_response(self, pkt): |
| if PAS5211MsgSetOpticsIoControlResponse in pkt: |
| log.debug("[RESPONSE] PAS5211MsgSetOpticsIoControl") |
| self.send_state[pkt.channel_id] = True |
| if self.check_channel_state(): |
| raise self.got_olt_io_optics() |
| raise self.wait_olt_io_optics() |
| |
| # Transitions got olt io optics |
| @ATMT.condition(got_olt_io_optics) |
| def check_downstream_pon_tx(self): |
| query = general_param(PON_TX_ENABLE_DEFAULT) |
| for id in CHANNELS: |
| self.send_state.append(False) |
| self.send(self.p(query, channel_id=id)) |
| log.debug("[SENT] PAS5211MsgGetGeneralParam") |
| |
| raise self.wait_query_response() |
| |
| # Transitions from wait query response |
| @ATMT.timeout(wait_query_response, 3) |
| def query_timeout(self): |
| log.error("Our queries have gone unanswered; disconnecting") |
| raise self.error() |
| |
| @ATMT.receive_condition(wait_query_response) |
| def check_pon_tx_state(self, pkt): |
| if PAS5211MsgGetGeneralParamResponse in pkt: |
| log.debug("[RESPONSE] PAS5211MsgGetGeneralParam") |
| self.send_state[pkt.channel_id] = True |
| if pkt.value == PON_ENABLE: |
| # TODO: we may want to do something here. |
| if self.check_channel_state(): |
| raise self.got_query_response() |
| else: |
| raise self.wait_query_response() |
| else: |
| log.error("TX downstream is not enabled") |
| raise self.error() |
| |
| # Transitions from got_query_response |
| @ATMT.condition(wait_query_response) |
| def send_add_olt(self): |
| olt_add = PAS5211MsgAddOltChannel() |
| for id in CHANNELS: |
| self.send_state.append(False) |
| self.send(self.p(olt_add, channel_id=id)) |
| log.debug("[SENT] PAS5211MsgAddOltChannel") |
| raise self.wait_olt_add() |
| |
| # Transitions from wait_olt_add |
| @ATMT.timeout(wait_olt_add, 3) |
| def olt_add_timeout(self): |
| log.error("Cannot add olts; disconnecting") |
| raise self.error() |
| |
| @ATMT.receive_condition(wait_olt_add) |
| def wait_for_olt_add(self, pkt): |
| if PAS5211MsgAddOltChannelResponse in pkt: |
| log.debug("[RESPONSE] PAS5211MsgAddOltChannel") |
| self.send_state[pkt.channel_id] = True |
| if self.check_channel_state(): |
| raise self.got_olt_add() |
| raise self.wait_olt_add() |
| |
| # Transitions from got_olt_add |
| @ATMT.condition(got_olt_add) |
| def send_alarm_config(self): |
| alarm_msg = alarm_config(PON_ALARM_LOS, PON_ENABLE) |
| for id in CHANNELS: |
| self.send_state.append(False) |
| self.send(self.p(alarm_msg, channel_id=id)) |
| log.debug("[SENT] PAS5211MsgSetAlarmConfig") |
| raise self.wait_alarm_set() |
| |
| # Transitions for wait_alarm_set |
| @ATMT.timeout(wait_alarm_set, 3) |
| def alarm_timeout(self): |
| log.error("Couldn't set alarms; disconnecting") |
| raise self.error() |
| |
| @ATMT.receive_condition(wait_alarm_set) |
| def wait_for_alarm_set(self, pkt): |
| if PAS5211MsgSetAlarmConfigResponse in pkt: |
| log.debug("[RESPONSE] PAS5211MsgSetAlarmConfig") |
| self.send_state[pkt.channel_id] = True |
| if self.check_channel_state(): |
| raise self.got_alarm_set() |
| raise self.wait_alarm_set() |
| |
| # Transitions from got_alarm_set |
| @ATMT.condition(got_alarm_set) |
| def send_dba_mode(self): |
| get_dba_mode = PAS5211MsgGetDbaMode() |
| for id in CHANNELS: |
| self.send_state.append(False) |
| self.send(self.p(get_dba_mode, channel_id=id)) |
| log.debug("[SENT] PAS5211MsgGetDbaMode") |
| |
| raise self.wait_dba_mode() |
| |
| # Transitions from wait_dba_mode |
| @ATMT.timeout(wait_dba_mode, 3) |
| def dba_timeout(self): |
| log.error("No DBA information returned; disconnecting") |
| raise self.error() |
| |
| @ATMT.receive_condition(wait_dba_mode) |
| def wait_for_dba_mode(self, pkt): |
| |
| if PAS5211MsgGetDbaModeResponse in pkt: |
| log.debug("[RESPONSE] PAS5211MsgGetDbaMode") |
| # TODO: What do we do in case the DBA is not loaded. |
| if pkt.dba_mode == PON_DBA_MODE_LOADED_NOT_RUNNING: |
| self.send_state[pkt.channel_id] = True |
| self.dba_needs_start = True |
| elif pkt.dba_mode == PON_DBA_MODE_RUNNING: |
| self.send_state[pkt.channel_id] = True |
| if self.check_channel_state(): |
| if self.dba_needs_start: |
| raise self.got_dba_mode() |
| else: |
| raise self.got_dba_start() |
| raise self.wait_dba_mode() |
| |
| # Transition from got_dba_mode |
| @ATMT.condition(got_dba_mode) |
| def send_start_dba(self): |
| dba_start = PAS5211MsgStartDbaAlgorithm( |
| size=0, initialization_data=None) |
| for id in CHANNELS: |
| self.send_state.append(False) |
| self.send(self.p(dba_start, channel_id=id)) |
| log.debug("[SENT] PAS5211MsgStartDbaAlgorithm") |
| raise self.wait_dba_start() |
| |
| # Transitions from wait_dba_start |
| @ATMT.timeout(wait_dba_start, 3) |
| def dba_start_timeout(self): |
| log.error("DBA has not started; disconnecting") |
| raise self.error() |
| |
| @ATMT.receive_condition(wait_dba_start) |
| def wait_for_dba_start(self, pkt): |
| if PAS5211MsgStartDbaAlgorithmResponse in pkt: |
| log.debug("[RESPONSE] PAS5211MsgStartDbaAlgorithm") |
| self.send_state[pkt.channel_id] = True |
| if self.check_channel_state(): |
| raise self.got_dba_start() |
| raise self.wait_dba_start() |
| |
| # Transitions from got_dba_start |
| @ATMT.condition(got_dba_start) |
| def send_activation_period(self): |
| activation = PAS5211MsgSetOltChannelActivationPeriod( |
| activation_period=1000) |
| for id in CHANNELS: |
| self.send_state.append(False) |
| self.send(self.p(activation, channel_id=id)) |
| log.debug("[SENT] PAS5211MsgSetOltChannelActivationPeriod") |
| |
| raise self.wait_activation() |
| |
| # Transitions for wait_for_activation |
| @ATMT.timeout(wait_activation, 3) |
| def timeout_activation(self): |
| log.error("No activation; disconnect") |
| raise self.error() |
| |
| @ATMT.receive_condition(wait_activation) |
| def wait_for_activation(self, pkt): |
| if PAS5211MsgSetOltChannelActivationPeriodResponse in pkt: |
| log.debug("[RESPONSE] PAS5211MsgSetOltChannelActivationPeriod") |
| self.send_state[pkt.channel_id] = True |
| if self.check_channel_state(): |
| log.info("Ruby OLT at {} initialised".format(self.target)) |
| # self.device.create_logical_device() |
| self.device.activate() |
| # self.device.add_upstream_port(129) |
| # self.device.add_logical_upstream_port(129) |
| # keep_alive = KeepAlive( |
| # iface=self.interface, comm=self.comm, target=self.target, device=self.device) |
| # keep_alive.run() |
| raise self.end() |
| raise self.wait_activation() |
| |
| |
| class KeepAlive(BaseOltAutomaton): |
| """ |
| Master filter: Only allow PAS5211MsgGetOltVersionResponse |
| """ |
| |
| def master_filter(self, pkt): |
| if not super(KeepAlive, self).master_filter(pkt): |
| return False |
| |
| if PAS5211MsgGetOltVersionResponse in pkt: |
| return True |
| |
| return False |
| |
| """ |
| States |
| """ |
| |
| @ATMT.state(initial=1) |
| def init_keepalive(self): |
| log.debug('init-keepalive') |
| |
| @ATMT.state() |
| def wait_keepalive(self): |
| pass |
| |
| @ATMT.state(error=1) |
| def error(self): |
| raise self.end() |
| |
| @ATMT.state(final=1) |
| def end(self): |
| log.debug('init-keepalive-end') |
| |
| """ |
| Transitions |
| """ |
| |
| # Send Keep Alive |
| @ATMT.condition(init_keepalive) |
| def send_keepalive(self): |
| self.send(self.p(PAS5211MsgGetOltVersion())) |
| raise self.got_keepalive() |
| |
| # Timeout |
| @ATMT.timeout(wait_keepalive, 1) |
| def timeout_keepalive(self): |
| log.error("OLT not responding to keep alive; disconnecting") |
| raise self.error() |
| |
| # Received Keep Alive |
| @ATMT.receive_condition(wait_keepalive) |
| def got_keepalive(self, pkt): |
| log.debug('keepalive-received') |
| raise self.send_keepalive() |