Skip to content

Commit

Permalink
Merge branch 'feature/gh92_create_hdmicec_module' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
TB-1993 committed Nov 1, 2024
2 parents 4a844c6 + 71e8e15 commit 8ad1723
Show file tree
Hide file tree
Showing 10 changed files with 738 additions and 58 deletions.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,17 @@ It understands how to:

### Requirements

- Python (=3.11.8)
- Python (version 3.10+)
- Pyenv can be used to manage multiple versions of Python (please refer to the [documentation](https://github.com/pyenv/pyenv?tab=readme-ov-file#installation)).

- All the packages listed in requirements.txt
- Can be installed using `$ pip install -r requirements.txt`

### User Installation

Clone the repository and ensure that all requirements are met.
Clone the repository and run the [`install_requirements.sh`](installation/install_requirements.sh) script.

Administrator rights are required to install the below packages. Without these some modules may not work:
- `cec-client` - Required to use the CECClient hdmiCECController module.

*If you do not have administrator rights, please ask your administrator to install the above packages for you.*

## Getting Started

Expand Down
66 changes: 30 additions & 36 deletions examples/configs/example_rack_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,27 +22,18 @@

# This config file creates your environment, defaults are setup internally unless otherwise overridden.
# you will need at least 1 rack and 1 slot to operate.
# each module e.g. console options, will be off by default unless otherwise stated

# Depreciated
# slotx: address: IP of device while running locally, replaced with slotx: ip
# slotx: deviceConsole: IP of device while running locally, replaced with slotx: devices
# Each optional module will be off by default unless otherwise stated.
# Uncomment and fill out the sections for the modules you require.

# Data that is global to all tests.
globalConfig:
includes:
# [ includes: ]
# [ deviceConfig: "required.yml file" ]
deviceConfig: "example_device_config.yml"
capture:
# [capture: optional]
# [ocrEnginePath: "/usr/bin/tesseract"] # "C:\\Program Files\\Tesseract-OCR\\tesseract.exe" (For Windows) - tesseract binary
# [resolution: "1080p"] - Capture resolution
# [input: 0] - which input is connected to the video path
# [capture: optional] - This section is required for use with the videoCapture module.
# [ocrEnginePath: "/usr/bin/tesseract"] # "C:\\Program Files\\Tesseract-OCR\\tesseract.exe" (For Windows) - tesseract binary
# [resolution: "1080p"] - Capture resolution
# [input: 0] - which input is connected to the video path
# Note: Video capture will not be installed unless screenRegions: is defined in deviceConfig:
ocrEnginePath: "/usr/bin/tesseract" # "C:\\Program Files\\Tesseract-OCR\\tesseract.exe" (For Windows)
resolution: "1080p"
input: 0
local:
log: # log for each slot
directory: "./logs"
Expand All @@ -57,37 +48,39 @@ rackConfig:
# [ name: "required", description: "optional"]
name: "slot1"
devices:
# [ devices: ]
# [ type: "serial": port: "COM7" baudRate: "(default)115200" dataBits: "optional(8)" stopBits: "optional(1)" parity: "optional(None)" FlowControl: "optional(None)" ]
# [ type: "ssh": port: 22 username: "test" password: "test" ]
# [ type: "telnet": port: 23 username: "test" password: "test" ]
- dut:
ip: "127.0.0.1" # IP Address of the ADA Hub
description: "local PC"
platform: "linux PC"
consoles:
# - [ name ] - consoles should be listed here with a name. Defined as one of the types below.
# supported types:
# [ type: "serial", port: "COM7", baudRate: "(default)115200", dataBits: "optional(8)", stopBits: "optional(1)", parity: "optional(None)", FlowControl: "optional(None)", ]
# [ type: "ssh", port: 22, username: "test", password: "test" ]
# [ type: "telnet", port: 23, username: "test", password: "test" ]
- default:
type: "serial"
port: "/dev/ttyUSB0"
- ssh:
port: 22
username: "root"
ip: "192.168.99.1"
remoteController:
# [ remoteController: ]
# [ type: "olimex" ip: "192.168.0.17" port: 7 map: "llama_rc6", config: "remote_commander.yml" ]
# [ type: "skyProc" map: "skyq_map", config: "remote_commander.yml" ]

# [ remoteController: optional ] - This section is required for use with the remoteController module.
# supported types:
# [ type: "olimex", ip: "192.168.0.17", port: 7, map: "llama_rc6", config: "remote_commander.yml" ]
# [ type: "skyProc", map: "skyq_map", config: "remote_commander.yml" ]
# [ type: "None" ]
type: "none"
map: "skyq_map"
config: "example_remote_commander.yml"
outbound:
download_url: "tftp://tftp-server.com/rack1/slot1/" # download location for the CPE device
upload_url: "sftp://server-address/home/workspace/tftp/rack1/slot1/" # upload location
upload_url_base_dir: "sftp://server-address/home/workspace/tftp/rack1/slot1" # alternative upload location
httpProxy: 'socks5h://localhost:1234' # Local Proxy if required
workspaceDirectory: './logs/workspace' # Local working directory
powerSwitch: # Specific power switch for each slot

# [ outbound: optional ] - This section is used to configure paths for downloads and uploads from your test
# supported usage:
# [download_url: "url" ] - download location for the CPE device
# [ upload_url: "url" ] - upload location
# [ upload_url_base_dir: "url" ] - alternative upload location
# [ httpProxy: "uri" ] - Local Proxy if required
# [ workspaceDirectory: "path to directory on host" ] - Local working directory

# [ powerSwitch: optional ] - Power switch for the slot
# supported types, if this section is undefined this is ok
# [type: "orvbioS20", ip: "", mac: "", port:"optional", relay:"optional"]
# [type: "kasa", ip: "", options:"--plug" ] # <- Plug
Expand All @@ -97,9 +90,10 @@ rackConfig:
# [type: "olimex", ip:"", port:"optional", relay:"" ]
# [type: "SLP", ip:"", username: "", password: "", outlet_id:"", port:"optional"]
# [type: "none" ] if section doesn't exist then type:none will be used
type: "HS100"
ip: "192.168.1.7"
port: 9999

# [ hdmiCECController: optional ] - Specific hdmiCECController for the slot
# supported types:
# [type: "cec-client", adaptor: "/dev/ttycec"]
- pi2:
ip: "192.168.99.1"
description: "local pi4"
Expand Down
4 changes: 4 additions & 0 deletions framework/core/deviceManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
from framework.core.powerControl import powerControlClass
from framework.core.outboundClient import outboundClientClass
from framework.core.commonRemote import commonRemoteClass
from framework.core.hdmiCECController import HDMICECController
from framework.core.utilities import utilities

dir_path = os.path.dirname(os.path.realpath(__file__))
Expand Down Expand Up @@ -174,6 +175,9 @@ def __init__(self, log:logModule, logPath:str, devices:dict):
config = device.get("remoteController")
if config != None:
self.remoteController = commonRemoteClass(log, config)
config = device.get("hdmiCECController")
if config != None:
self.hdmiCECController = HDMICECController(log, config)
self.session = self.getConsoleSession()

def getField(self, fieldName:str, itemsList:dict = None):
Expand Down
170 changes: 170 additions & 0 deletions framework/core/hdmiCECController.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
#!/usr/bin/env python3
#** *****************************************************************************
# *
# * If not stated otherwise in this file or this component's LICENSE file the
# * following copyright and licenses apply:
# *
# * Copyright 2023 RDK Management
# *
# * 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.
# *
#* ******************************************************************************
#*
#* ** Project : RAFT
#* ** @addtogroup : core
#* ** @date : 22/11/2021
#* **
#* ** @brief : HDMICECController class to differentiate into the whichever
#* ** cec controller type is specified.
#* **
#* ******************************************************************************

from os import path

import sys
MY_PATH = path.realpath(__file__)
MY_DIR = path.dirname(MY_PATH)
sys.path.append(path.join(MY_DIR,'../../'))
from framework.core.logModule import logModule
from hdmicecModules import CECClientController, MonitoringType

class HDMICECController():
"""
This class provides a high-level interface for controlling and monitoring
Consumer Electronics Control (CEC) devices.
"""

def __init__(self, log: logModule, config: dict):
"""
Initializes the HDMICECController instance.
Args:
log (logModule): An instance of a logging module for recording messages.
config (dict): A dictionary containing configuration options
"""
self._log = log
self.controllerType = config.get('type')
self.cecAdaptor = config.get('adaptor')
if self.controllerType.lower() == 'cec-client':
self.controller = CECClientController(self.cecAdaptor, self._log)
self._read_line = 0
self._monitoringLog = path.join(self._log.logPath, 'cecMonitor.log')

def send_message(self, message: str) -> bool:
"""
Sends a CEC message to connected devices using the configured controller.
Args:
message (str): The CEC message to be sent.
Returns:
bool: True if the message was sent successfully, False otherwise.
"""
self._log.debug('Sending CEC message: [%s]' % message)
return self.controller.sendMessage(message)

def startMonitoring(self, deviceType: MonitoringType = MonitoringType.RECORDER) -> None:
"""
Starts monitoring CEC messages from the adaptor as the specified device type.
Args:
deviceType (MonitoringType, optional): The type of device to monitor (default: MonitoringType.RECORDER).
Raises:
RuntimeError: If monitoring is already running.
"""
if self.controller.monitoring is False:
self._log.debug('Starting monitoring on adaptor: [%s]' % self.cecAdaptor)
self._log.debug('Monitoring as device type [%s]' % deviceType.name)
return self.controller.startMonitoring(self._monitoringLog, deviceType)
else:
self._log.warn('CEC monitoring is already running')

def stopMonitoring(self):
"""
Stops the CEC monitoring process.
Delegates the stop task to the underlying `CECClientController`.
"""
return self.controller.stopMonitoring()

def readUntil(self, message: str, retries: int = 5) -> bool:
"""
Reads the monitoring log until the specified message is found.
Opens the monitoring log file and checks for the message within a specified retry limit.
Args:
message (str): The message to search for in the monitoring log.
retries (int, optional): The maximum number of retries before giving up (default: 5).
Returns:
bool: True if the message was found, False otherwise.
"""
self._log.debug('Starting readUntil for message as [%s] with [%s] retries' % (message,retries))
result = False
retry = 0
max_retries = retries
while retry != max_retries and not result:
with open(self._monitoringLog, 'r') as logFile:
logLines = logFile.readlines()
read_line = self._read_line
write_line = len(logLines)
while read_line != write_line:
if message in logLines[read_line]:
result = True
break
read_line+=1
retry += 1
self._read_line = read_line
return result

def listDevices(self) -> list:
"""
Retrieves a list of discovered CEC devices with their OSD names (if available).
Returns:
list: A list of dictionaries representing discovered devices.
"""
self._log.debug('Listing devices on CEC network')
return self.controller.listDevices()


if __name__ == "__main__":
import time
import json
LOG = logModule('CECTEST', logModule.DEBUG)
CONFIGS = [
{
'type': 'cec-client',
'adaptor': '/dev/ttyACM0'
},
]
for config in CONFIGS:
LOG.setFilename('./logs/','CECTEST%s.log' % config.get('type'))
LOG.stepStart('Testing with %s' % json.dumps(config))
CEC = HDMICECController(LOG, config)
DEVICES = CEC.listDevices()
LOG.info(json.dumps(DEVICES))
# The user will need to check all the devices expected from their
# cec network are shown in this output.
CEC.startMonitoring()
# It's is expected that a user will send a standby command on their cec
# network during this 2 minutes.
time.sleep(120)
result = CEC.readUntil('standby')
CEC.stopMonitoring()
LOG.stepResult(result, 'The readUntil result is: [%s]' % result)
# The user should check here the monitoring log for thier type contains
# the expected information.
30 changes: 30 additions & 0 deletions framework/core/hdmicecModules/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#** *****************************************************************************
# *
# * If not stated otherwise in this file or this component's LICENSE file the
# * following copyright and licenses apply:
# *
# * Copyright 2023 RDK Management
# *
# * 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.
# *
#* ******************************************************************************
#*
#* ** Project : RAFT
#* ** @addtogroup : core
#* ** @date : 22/11/2021
#* **
#* ******************************************************************************

from .cecClient import CECClientController
from .cecTypes import MonitoringType
Loading

0 comments on commit 8ad1723

Please sign in to comment.