blob: 14b70d5bb812ca74b8ecf234d1328574ce565651 [file] [log] [blame]
Girish Gowdra6afb56a2021-04-27 17:47:57 -07001/*
Joey Armstrong89c812c2024-01-12 19:00:20 -05002 * Copyright 2021-2024 Open Networking Foundation (ONF) and the ONF Contributors
Girish Gowdra6afb56a2021-04-27 17:47:57 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Joey Armstrong89c812c2024-01-12 19:00:20 -050017// Package omcitst provides the omci test functionality
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +000018package omcitst
Girish Gowdra6afb56a2021-04-27 17:47:57 -070019
20import (
21 "context"
22 "fmt"
khenaidoo7d3c5582021-08-11 18:09:44 -040023 "sync"
24 "time"
25
Girish Gowdra6afb56a2021-04-27 17:47:57 -070026 "github.com/looplab/fsm"
mpagenko836a1fd2021-11-01 16:12:42 +000027 "github.com/opencord/omci-lib-go/v2"
28 "github.com/opencord/omci-lib-go/v2/generated"
khenaidoo7d3c5582021-08-11 18:09:44 -040029 "github.com/opencord/voltha-lib-go/v7/pkg/log"
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +000030 cmn "github.com/opencord/voltha-openonu-adapter-go/internal/pkg/common"
khenaidoo7d3c5582021-08-11 18:09:44 -040031 "github.com/opencord/voltha-protos/v5/go/extension"
Girish Gowdra6afb56a2021-04-27 17:47:57 -070032)
33
34const (
35 // events of Self Test FSM
36 selfTestEventTestRequest = "selfTestEventTestRequest"
37 selfTestEventTestResponseSuccess = "selfTestEventTestResponseSuccess"
38 selfTestEventTestResultSuccess = "selfTestEventTestResultSuccess"
39 selfTestEventAbort = "selfTestEventAbort"
40)
41const (
42 // states of Self Test FSM
43 selfTestStNull = "selfTestStNull"
44 selfTestStHandleSelfTestReq = "selfTestStHandleSelfTestReq"
45 selfTestStHandleSelfTestResp = "selfTestStHandleSelfTestResp"
46 selfTestStHandleTestResult = "selfTestStHandleTestResult"
47)
48
49const (
50 //SelfTestResponseWaitTimeout specifies timeout value waiting for self test response. Unit in seconds
51 SelfTestResponseWaitTimeout = 2
52)
53
54// We initiate an fsmCb per Self Test Request
55type fsmCb struct {
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +000056 fsm *cmn.AdapterFsm
Girish Gowdra6afb56a2021-04-27 17:47:57 -070057 respChan chan extension.SingleGetValueResponse
58 stopOmciChan chan bool
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +053059 reqMsg extension.SingleGetValueRequest
Girish Gowdra6afb56a2021-04-27 17:47:57 -070060}
61
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +000062// SelfTestControlBlock - TODO: add comment
63type SelfTestControlBlock struct {
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +000064 pDeviceHandler cmn.IdeviceHandler
65 pDevEntry cmn.IonuDeviceEntry
Girish Gowdra6afb56a2021-04-27 17:47:57 -070066
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +053067 selfTestFsmMap map[generated.ClassID]*fsmCb // The fsmCb is indexed by ME Class ID of the Test Action procedure
68 StopSelfTestModule chan bool
69 deviceID string
70 selfTestFsmLock sync.RWMutex
Girish Gowdra6afb56a2021-04-27 17:47:57 -070071
Sridhar Ravindraf1331ad2024-02-15 16:13:37 +053072 SelfTestHandlerLock sync.RWMutex
73 SelfTestHandlerActive bool
Girish Gowdra6afb56a2021-04-27 17:47:57 -070074}
75
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +000076// NewSelfTestMsgHandlerCb creates the SelfTestControlBlock
Girish Gowdra6afb56a2021-04-27 17:47:57 -070077// Self Test Handler module supports sending SelfTestRequest and handling of SelfTestResponse/SelfTestResults
78// An ephemeral Self Test FSM is initiated for every Self Test request and multiple Self Tests on different
79// MEs (that support it) can be handled in parallel.
80// At the time of creating this module, only ANI-G self-test is supported.
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +000081func NewSelfTestMsgHandlerCb(ctx context.Context, dh cmn.IdeviceHandler, devEntry cmn.IonuDeviceEntry) *SelfTestControlBlock {
82 selfTestCb := SelfTestControlBlock{
83 deviceID: dh.GetDeviceID(),
84 pDeviceHandler: dh,
85 pDevEntry: devEntry,
86 }
Girish Gowdra6afb56a2021-04-27 17:47:57 -070087 selfTestCb.selfTestFsmMap = make(map[generated.ClassID]*fsmCb)
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +000088 selfTestCb.StopSelfTestModule = make(chan bool)
Girish Gowdra6afb56a2021-04-27 17:47:57 -070089
90 go selfTestCb.waitForStopSelfTestModuleSignal(ctx)
91
92 return &selfTestCb
93}
94
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +000095func (selfTestCb *SelfTestControlBlock) initiateNewSelfTestFsm(ctx context.Context, reqMsg extension.SingleGetValueRequest,
96 CommChan chan cmn.Message, classID generated.ClassID, respChan chan extension.SingleGetValueResponse) error {
97 aFsm := cmn.NewAdapterFsm("selfTestFsm", selfTestCb.deviceID, CommChan)
Girish Gowdra6afb56a2021-04-27 17:47:57 -070098
99 if aFsm == nil {
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000100 logger.Errorw(ctx, "selfTestFsm cmn.AdapterFsm could not be instantiated!!", log.Fields{
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700101 "device-id": selfTestCb.deviceID})
102 return fmt.Errorf("nil-adapter-fsm")
103 }
104 // Self Test FSM related state machine
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000105 aFsm.PFsm = fsm.NewFSM(
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700106
107 selfTestStNull,
108 fsm.Events{
109 {Name: selfTestEventTestRequest, Src: []string{selfTestStNull}, Dst: selfTestStHandleSelfTestReq},
110 {Name: selfTestEventTestResponseSuccess, Src: []string{selfTestStHandleSelfTestReq}, Dst: selfTestStHandleSelfTestResp},
111 {Name: selfTestEventTestResultSuccess, Src: []string{selfTestStHandleSelfTestResp}, Dst: selfTestStNull},
bseeniva6db14a22024-12-07 19:41:01 +0530112 {Name: selfTestEventAbort, Src: []string{selfTestStHandleSelfTestReq, selfTestStHandleSelfTestResp, selfTestStHandleTestResult,
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000113 selfTestStNull}, Dst: selfTestStNull},
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700114 },
115 fsm.Callbacks{
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000116 "enter_state": func(e *fsm.Event) { aFsm.LogFsmStateChange(ctx, e) },
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700117 "enter_" + selfTestStHandleSelfTestReq: func(e *fsm.Event) { selfTestCb.selfTestFsmHandleSelfTestRequest(ctx, e) },
118 "enter_" + selfTestStHandleSelfTestResp: func(e *fsm.Event) { selfTestCb.selfTestFsmHandleSelfTestResponse(ctx, e) },
119 },
120 )
121 selfTestCb.selfTestFsmLock.Lock()
122 selfTestCb.selfTestFsmMap[classID] = &fsmCb{fsm: aFsm, reqMsg: reqMsg, respChan: respChan, stopOmciChan: make(chan bool)}
123 // Initiate the selfTestEventTestRequest on the FSM. Also pass the additional argument - classID.
124 // This is useful for the the FSM handler function to pull out fsmCb from the selfTestCb.selfTestFsmMap map.
125 selfTestCb.triggerFsmEvent(aFsm, selfTestEventTestRequest, classID)
126 selfTestCb.selfTestFsmLock.Unlock()
127
128 return nil
129}
130
131///// FSM Handlers
132
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000133func (selfTestCb *SelfTestControlBlock) selfTestFsmHandleSelfTestRequest(ctx context.Context, e *fsm.Event) {
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700134 classID := e.Args[0].(generated.ClassID)
135 selfTestCb.selfTestFsmLock.RLock()
136 pFsmCb, ok := selfTestCb.selfTestFsmMap[classID]
137 selfTestCb.selfTestFsmLock.RUnlock()
138 if !ok {
139 // This case is impossible. Would be curious to see if this happens
140 logger.Fatalw(ctx, "class-id-not-found", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
141 }
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000142 instKeys := selfTestCb.pDevEntry.GetOnuDB().GetSortedInstKeys(ctx, classID)
Akash Reddy Kankanalabe650282025-07-14 12:19:10 +0530143 if len(instKeys) == 0 {
144 logger.Errorw(ctx, "no instances found for class id", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
145 selfTestCb.triggerFsmEvent(pFsmCb.fsm, selfTestEventAbort)
146 selfTestCb.submitFailureGetValueResponse(ctx, pFsmCb.respChan, extension.GetValueResponse_INTERNAL_ERROR, extension.GetValueResponse_ERROR, pFsmCb.reqMsg)
147 return
148 }
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000149 // TODO: Choosing the first index from the instance keys. For ANI-G, this is fine as there is only one ANI-G instance.
150 // How do we handle and report self test for multiple instances?
151 if err := selfTestCb.pDevEntry.GetDevOmciCC().SendSelfTestReq(ctx, classID, instKeys[0], selfTestCb.pDeviceHandler.GetOmciTimeout(),
152 false, pFsmCb.fsm.CommChan); err != nil {
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700153 logger.Errorw(ctx, "error sending self test request", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
154 selfTestCb.triggerFsmEvent(pFsmCb.fsm, selfTestEventAbort)
bseeniva6db14a22024-12-07 19:41:01 +0530155 selfTestCb.submitFailureGetValueResponse(ctx, pFsmCb.respChan, extension.GetValueResponse_INTERNAL_ERROR, extension.GetValueResponse_ERROR, pFsmCb.reqMsg)
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700156 return
157 }
158
159 go selfTestCb.handleOmciResponse(ctx, classID)
160}
161
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000162func (selfTestCb *SelfTestControlBlock) selfTestFsmHandleSelfTestResponse(ctx context.Context, e *fsm.Event) {
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700163 classID := e.Args[0].(generated.ClassID)
164 // Pass the test result processing to another routine
165 go selfTestCb.handleOmciResponse(ctx, classID)
166
167}
168
169///// Utility functions
170
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000171func (selfTestCb *SelfTestControlBlock) getMeClassID(ctx context.Context, reqMsg extension.SingleGetValueRequest) (generated.ClassID, error) {
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700172 switch reqMsg.GetRequest().GetRequest().(type) {
173 case *extension.GetValueRequest_OnuOpticalInfo:
mgoudad611f4c2025-10-30 14:49:27 +0530174 return generated.AniGClassID, nil
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700175 default:
176 logger.Warnw(ctx, "unsupported me class id for self test", log.Fields{"device-id": selfTestCb.deviceID})
177 return 0, fmt.Errorf("unsupported me class id for self test %v", selfTestCb.deviceID)
178 }
179}
180
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000181func (selfTestCb *SelfTestControlBlock) triggerFsmEvent(pSelfTestFsm *cmn.AdapterFsm, event string, args ...generated.ClassID) {
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700182 go func() {
183 if len(args) > 0 {
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000184 _ = pSelfTestFsm.PFsm.Event(event, args[0])
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700185 } else {
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000186 _ = pSelfTestFsm.PFsm.Event(event)
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700187 }
188 }()
189}
190
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530191//nolint:unparam
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000192func (selfTestCb *SelfTestControlBlock) submitFailureGetValueResponse(ctx context.Context, respChan chan extension.SingleGetValueResponse,
bseeniva6db14a22024-12-07 19:41:01 +0530193 errorCode extension.GetValueResponse_ErrorReason, statusCode extension.GetValueResponse_Status, reqMsg extension.SingleGetValueRequest) {
194 meClassID, err := selfTestCb.getMeClassID(ctx, reqMsg)
195 if err != nil {
196 return
197 }
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700198 singleValResp := extension.SingleGetValueResponse{
199 Response: &extension.GetValueResponse{
200 Status: statusCode,
201 ErrReason: errorCode,
202 },
203 }
204 logger.Infow(ctx, "OMCI test response failure - pushing failure response", log.Fields{"device-id": selfTestCb.deviceID})
bseeniva6db14a22024-12-07 19:41:01 +0530205 // Clear the fsmCb from the map
206 delete(selfTestCb.selfTestFsmMap, meClassID)
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700207 respChan <- singleValResp
208 logger.Infow(ctx, "OMCI test response failure - pushing failure response complete", log.Fields{"device-id": selfTestCb.deviceID})
209}
210
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000211func (selfTestCb *SelfTestControlBlock) handleOmciMessage(ctx context.Context, msg cmn.OmciMessage, cb *fsmCb, classID generated.ClassID) {
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700212 logger.Debugw(ctx, "omci Msg", log.Fields{"device-id": selfTestCb.deviceID, "msgType": msg.OmciMsg.MessageType, "msg": msg})
213 switch msg.OmciMsg.MessageType {
214 case omci.TestResponseType:
215 selfTestCb.handleOmciTestResponse(ctx, msg, cb, classID)
216 case omci.TestResultType:
217 selfTestCb.handleOmciTestResult(ctx, msg, cb, classID)
218 default:
219 logger.Warnw(ctx, "Unknown Message Type", log.Fields{"msgType": msg.OmciMsg.MessageType})
220 selfTestCb.triggerFsmEvent(cb.fsm, selfTestEventAbort)
bseeniva6db14a22024-12-07 19:41:01 +0530221 selfTestCb.submitFailureGetValueResponse(ctx, cb.respChan, extension.GetValueResponse_UNSUPPORTED, extension.GetValueResponse_ERROR, cb.reqMsg)
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700222 }
223}
224
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000225func (selfTestCb *SelfTestControlBlock) handleOmciTestResponse(ctx context.Context, msg cmn.OmciMessage, cb *fsmCb, classID generated.ClassID) {
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700226 msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeTestResponse)
227 if msgLayer == nil {
228 logger.Errorw(ctx, "omci Msg layer nil self test response", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000229 selfTestCb.pDevEntry.GetDevOmciCC().ReleaseTid(ctx, msg.OmciMsg.TransactionID)
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700230 selfTestCb.triggerFsmEvent(cb.fsm, selfTestEventAbort)
bseeniva6db14a22024-12-07 19:41:01 +0530231 selfTestCb.submitFailureGetValueResponse(ctx, cb.respChan, extension.GetValueResponse_INTERNAL_ERROR, extension.GetValueResponse_ERROR, cb.reqMsg)
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700232 return
233 }
234 msgObj, msgOk := msgLayer.(*omci.TestResponse)
235 if !msgOk {
236 logger.Errorw(ctx, "omci Msg layer could not be detected for self test response", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000237 selfTestCb.pDevEntry.GetDevOmciCC().ReleaseTid(ctx, msg.OmciMsg.TransactionID)
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700238 selfTestCb.triggerFsmEvent(cb.fsm, selfTestEventAbort)
bseeniva6db14a22024-12-07 19:41:01 +0530239 selfTestCb.submitFailureGetValueResponse(ctx, cb.respChan, extension.GetValueResponse_INTERNAL_ERROR, extension.GetValueResponse_ERROR, cb.reqMsg)
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700240 return
241 }
242 logger.Debugw(ctx, "OMCI test response Data", log.Fields{"device-id": selfTestCb.deviceID, "data-fields": msgObj})
243 if msgObj.Result == generated.Success && msgObj.EntityClass == classID {
244 logger.Infow(ctx, "OMCI test response success", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
245 selfTestCb.triggerFsmEvent(cb.fsm, selfTestEventTestResponseSuccess, classID)
246 return
247 }
248
249 logger.Infow(ctx, "OMCI test response failure", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000250 selfTestCb.pDevEntry.GetDevOmciCC().ReleaseTid(ctx, msg.OmciMsg.TransactionID)
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700251 selfTestCb.triggerFsmEvent(cb.fsm, selfTestEventAbort)
bseeniva6db14a22024-12-07 19:41:01 +0530252 selfTestCb.submitFailureGetValueResponse(ctx, cb.respChan, extension.GetValueResponse_UNSUPPORTED, extension.GetValueResponse_ERROR, cb.reqMsg)
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700253}
254
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000255func (selfTestCb *SelfTestControlBlock) handleOmciTestResult(ctx context.Context, msg cmn.OmciMessage, cb *fsmCb, classID generated.ClassID) {
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700256 msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeTestResult)
257 if msgLayer == nil {
258 logger.Errorw(ctx, "omci Msg layer nil self test result", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
259 selfTestCb.triggerFsmEvent(cb.fsm, selfTestEventAbort)
bseeniva6db14a22024-12-07 19:41:01 +0530260 selfTestCb.submitFailureGetValueResponse(ctx, cb.respChan, extension.GetValueResponse_INTERNAL_ERROR, extension.GetValueResponse_ERROR, cb.reqMsg)
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700261 return
262 }
263 var msgObj *omci.OpticalLineSupervisionTestResult
264 var msgOk bool
265 switch classID {
mgoudad611f4c2025-10-30 14:49:27 +0530266 case generated.AniGClassID:
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700267 msgObj, msgOk = msgLayer.(*omci.OpticalLineSupervisionTestResult)
268 default:
269 // We should not really land here
270 selfTestCb.triggerFsmEvent(cb.fsm, selfTestEventAbort)
bseeniva6db14a22024-12-07 19:41:01 +0530271 selfTestCb.submitFailureGetValueResponse(ctx, cb.respChan, extension.GetValueResponse_INTERNAL_ERROR, extension.GetValueResponse_ERROR, cb.reqMsg)
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700272 return
273 }
274 if !msgOk {
275 logger.Errorw(ctx, "omci Msg layer could not be detected for self test result", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
276 selfTestCb.triggerFsmEvent(cb.fsm, selfTestEventAbort)
bseeniva6db14a22024-12-07 19:41:01 +0530277 selfTestCb.submitFailureGetValueResponse(ctx, cb.respChan, extension.GetValueResponse_INTERNAL_ERROR, extension.GetValueResponse_ERROR, cb.reqMsg)
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700278 return
279 }
280 logger.Debugw(ctx, "raw omci values of ani-g test result",
281 log.Fields{"device-id": selfTestCb.deviceID,
282 "power-feed-voltage": msgObj.PowerFeedVoltage,
283 "rx-power": msgObj.ReceivedOpticalPower,
284 "tx-power": msgObj.MeanOpticalLaunch,
285 "laser-bias-current": msgObj.LaserBiasCurrent,
286 "temperature": msgObj.Temperature})
287 singleValResp := extension.SingleGetValueResponse{
288 Response: &extension.GetValueResponse{
289 Status: extension.GetValueResponse_OK,
290 Response: &extension.GetValueResponse_OnuOpticalInfo{
291 OnuOpticalInfo: &extension.GetOnuPonOpticalInfoResponse{
292 // OMCI representation is Volts, 2s compliment, 20mV resolution
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000293 PowerFeedVoltage: float32(cmn.TwosComplementToSignedInt16(msgObj.PowerFeedVoltage)) * 0.02,
Girish Gowdra444522f2021-05-12 14:32:24 -0700294 // OMCI representation is Decibel-microwatts, 2s compliment, 0.002dB resolution.
295 // Subtract 30 to convert the unit from dBu to dBm (as expected by proto interface)
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000296 ReceivedOpticalPower: float32(cmn.TwosComplementToSignedInt16(msgObj.ReceivedOpticalPower))*0.002 - 30,
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700297 // OMCI representation is Decibel-microwatts, 2s compliment, 0.002dB resolution
Girish Gowdra444522f2021-05-12 14:32:24 -0700298 // Subtract 30 to convert the unit from dBu to dBm (as expected by proto interface)
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000299 MeanOpticalLaunchPower: float32(cmn.TwosComplementToSignedInt16(msgObj.MeanOpticalLaunch))*0.002 - 30,
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700300 // OMCI representation is unsigned int, 2uA resolution
301 // units of gRPC interface is mA.
302 LaserBiasCurrent: float32(msgObj.LaserBiasCurrent) * 0.000002 * 1000, // multiply by 1000 to get units in mA
303 // OMCI representation is 2s complement, 1/256 degree Celsius resolution
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000304 Temperature: float32(cmn.TwosComplementToSignedInt16(msgObj.Temperature)) / 256.0,
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700305 },
306 },
307 },
308 }
309 logger.Debugw(ctx, "ani-g test result after type/value conversion",
310 log.Fields{"device-id": selfTestCb.deviceID,
311 "power-feed-voltage": singleValResp.Response.GetOnuOpticalInfo().PowerFeedVoltage,
312 "rx-power": singleValResp.Response.GetOnuOpticalInfo().ReceivedOpticalPower,
313 "tx-power": singleValResp.Response.GetOnuOpticalInfo().MeanOpticalLaunchPower,
314 "laser-bias-current": singleValResp.Response.GetOnuOpticalInfo().LaserBiasCurrent,
315 "temperature": singleValResp.Response.GetOnuOpticalInfo().Temperature})
316 selfTestCb.triggerFsmEvent(cb.fsm, selfTestEventTestResultSuccess)
317 logger.Infow(ctx, "OMCI test result success - pushing results", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
318 cb.respChan <- singleValResp
319 selfTestCb.selfTestRequestComplete(ctx, cb.reqMsg)
320 logger.Infow(ctx, "OMCI test result success - pushing results complete", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
321}
322
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000323func (selfTestCb *SelfTestControlBlock) handleOmciResponse(ctx context.Context, classID generated.ClassID) {
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700324 selfTestCb.selfTestFsmLock.RLock()
325 pFsmCb, ok := selfTestCb.selfTestFsmMap[classID]
326 selfTestCb.selfTestFsmLock.RUnlock()
327 if !ok {
328 logger.Errorw(ctx, "fsb control block unavailable", log.Fields{"device-id": selfTestCb.deviceID, "class-id": classID})
329 return
330 }
331 select {
332 case <-pFsmCb.stopOmciChan:
333 logger.Infow(ctx, "omci processing stopped", log.Fields{"device-id": selfTestCb.deviceID, "class-id": classID})
334 selfTestCb.triggerFsmEvent(pFsmCb.fsm, selfTestEventAbort)
bseeniva6db14a22024-12-07 19:41:01 +0530335 selfTestCb.submitFailureGetValueResponse(ctx, pFsmCb.respChan, extension.GetValueResponse_REASON_UNDEFINED, extension.GetValueResponse_ERROR, pFsmCb.reqMsg)
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000336 case message, ok := <-pFsmCb.fsm.CommChan:
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700337 if !ok {
338 logger.Errorw(ctx, "Message couldn't be read from channel", log.Fields{"device-id": selfTestCb.deviceID})
339 selfTestCb.triggerFsmEvent(pFsmCb.fsm, selfTestEventAbort)
bseeniva6db14a22024-12-07 19:41:01 +0530340 selfTestCb.submitFailureGetValueResponse(ctx, pFsmCb.respChan, extension.GetValueResponse_INTERNAL_ERROR, extension.GetValueResponse_ERROR, pFsmCb.reqMsg)
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700341 }
342 logger.Debugw(ctx, "Received message on self test result channel", log.Fields{"device-id": selfTestCb.deviceID})
343
344 switch message.Type {
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000345 case cmn.OMCI:
346 msg, _ := message.Data.(cmn.OmciMessage)
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700347 selfTestCb.handleOmciMessage(ctx, msg, pFsmCb, classID)
348 default:
349 logger.Errorw(ctx, "Unknown message type received", log.Fields{"device-id": selfTestCb.deviceID, "message.Type": message.Type})
bseeniva6db14a22024-12-07 19:41:01 +0530350 selfTestCb.submitFailureGetValueResponse(ctx, pFsmCb.respChan, extension.GetValueResponse_UNSUPPORTED, extension.GetValueResponse_ERROR, pFsmCb.reqMsg)
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700351 }
352 case <-time.After(time.Duration(SelfTestResponseWaitTimeout) * time.Second):
353 logger.Errorw(ctx, "timeout waiting for test result", log.Fields{"device-id": selfTestCb.deviceID, "classID": classID})
354 selfTestCb.triggerFsmEvent(pFsmCb.fsm, selfTestEventAbort)
bseeniva6db14a22024-12-07 19:41:01 +0530355 selfTestCb.submitFailureGetValueResponse(ctx, pFsmCb.respChan, extension.GetValueResponse_TIMEOUT, extension.GetValueResponse_ERROR, pFsmCb.reqMsg)
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700356 }
357}
358
359// selfTestRequestComplete removes the fsmCb from the local cache if found
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000360func (selfTestCb *SelfTestControlBlock) selfTestRequestComplete(ctx context.Context, reqMsg extension.SingleGetValueRequest) {
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700361 meClassID, err := selfTestCb.getMeClassID(ctx, reqMsg)
362 if err != nil {
363 return
364 }
365 logger.Infow(ctx, "self test req handling complete", log.Fields{"device-id": selfTestCb.deviceID, "meClassID": meClassID})
366 // Clear the fsmCb from the map
367 delete(selfTestCb.selfTestFsmMap, meClassID)
368}
369
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000370func (selfTestCb *SelfTestControlBlock) waitForStopSelfTestModuleSignal(ctx context.Context) {
Girish Gowdra10123c02021-08-30 11:52:06 -0700371 selfTestCb.SetSelfTestHandlerIsRunning(true)
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000372 <-selfTestCb.StopSelfTestModule // block on stop signal
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700373
374 logger.Infow(ctx, "received stop signal - clean up start", log.Fields{"device-id": selfTestCb.deviceID})
375 selfTestCb.selfTestFsmLock.Lock()
376 for classID, fsmCb := range selfTestCb.selfTestFsmMap {
377 select {
378 case fsmCb.stopOmciChan <- true: // stop omci processing routine if one was active. It eventually aborts the fsm
379 logger.Debugw(ctx, "stopped omci processing", log.Fields{"device-id": selfTestCb.deviceID, "meClassID": classID})
380 default:
381 selfTestCb.triggerFsmEvent(fsmCb.fsm, selfTestEventAbort)
bseeniva6db14a22024-12-07 19:41:01 +0530382 selfTestCb.submitFailureGetValueResponse(ctx, fsmCb.respChan, extension.GetValueResponse_REASON_UNDEFINED, extension.GetValueResponse_ERROR, fsmCb.reqMsg)
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700383 }
384 }
385 selfTestCb.selfTestFsmMap = make(map[generated.ClassID]*fsmCb) // reset map
386 selfTestCb.selfTestFsmLock.Unlock()
387 logger.Infow(ctx, "received stop signal - clean up end", log.Fields{"device-id": selfTestCb.deviceID})
388}
389
390//// Exported functions
391
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000392// SetSelfTestHandlerIsRunning sets the value to selfTestCb.selfTestHandlerActive
393func (selfTestCb *SelfTestControlBlock) SetSelfTestHandlerIsRunning(active bool) {
Sridhar Ravindraf1331ad2024-02-15 16:13:37 +0530394 selfTestCb.SelfTestHandlerLock.Lock()
395 defer selfTestCb.SelfTestHandlerLock.Unlock()
396 selfTestCb.SelfTestHandlerActive = active
Girish Gowdra10123c02021-08-30 11:52:06 -0700397}
398
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000399// GetSelfTestHandlerIsRunning gets selfTestCb.selfTestHandlerActive
400func (selfTestCb *SelfTestControlBlock) GetSelfTestHandlerIsRunning() bool {
Sridhar Ravindraf1331ad2024-02-15 16:13:37 +0530401 selfTestCb.SelfTestHandlerLock.RLock()
402 defer selfTestCb.SelfTestHandlerLock.RUnlock()
403 return selfTestCb.SelfTestHandlerActive
Girish Gowdra10123c02021-08-30 11:52:06 -0700404}
405
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000406// SelfTestRequestStart initiate Test Request handling procedure. The results are asynchronously conveyed on the respChan.
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700407// If the return from selfTestRequest is NOT nil, the caller shall not wait for async response.
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000408func (selfTestCb *SelfTestControlBlock) SelfTestRequestStart(ctx context.Context, reqMsg extension.SingleGetValueRequest,
409 CommChan chan cmn.Message, respChan chan extension.SingleGetValueResponse) error {
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700410 meClassID, err := selfTestCb.getMeClassID(ctx, reqMsg)
411 if err != nil {
412 return err
413 }
414 if _, ok := selfTestCb.selfTestFsmMap[meClassID]; ok {
415 logger.Errorw(ctx, "self test already in progress for class id", log.Fields{"device-id": selfTestCb.deviceID, "class-id": meClassID})
416 return fmt.Errorf("self-test-already-in-progress-for-class-id-%v-device-id-%v", meClassID, selfTestCb.deviceID)
417 }
418 logger.Infow(ctx, "self test request initiated", log.Fields{"device-id": selfTestCb.deviceID, "meClassID": meClassID})
419 // indicates only if the FSM was initiated correctly. Response is asynchronous on respChan.
420 // If the return from here is NOT nil, the caller shall not wait for async response.
Holger Hildebrandt4b5e73f2021-08-19 06:51:21 +0000421 return selfTestCb.initiateNewSelfTestFsm(ctx, reqMsg, CommChan, meClassID, respChan)
Girish Gowdra6afb56a2021-04-27 17:47:57 -0700422}
Holger Hildebrandte7cc6092022-02-01 11:37:03 +0000423
424// PrepareForGarbageCollection - remove references to prepare for garbage collection
425func (selfTestCb *SelfTestControlBlock) PrepareForGarbageCollection(ctx context.Context, aDeviceID string) {
426 logger.Debugw(ctx, "prepare for garbage collection", log.Fields{"device-id": aDeviceID})
427 selfTestCb.pDeviceHandler = nil
428 selfTestCb.pDevEntry = nil
429}