1. Introduction
The Serial Cables HYDRA Controller is a comprehensive Python library designed for controlling and monitoring Serial Cables PCIe Gen6 HYDRA 8-Bay Passive JBOF (Just a Bunch of Flash) enclosures. This library provides complete hardware control, environmental monitoring, and alerting capabilities through a serial CLI interface.
Key Features
- Complete Hardware Control: Full implementation of all documented MCU commands
- Environmental Monitoring: Real-time temperature, voltage, current, and power monitoring
- SSD Slot Management: Individual and bulk control of all 8 SSD slots
- LED Control: Host and fault LED management for visual status indication
- Fan Control: PWM speed control and monitoring of cooling fans
- I2C/SMBus Communication: Direct device access for advanced diagnostics
- Comprehensive Logging: CSV data logging for long-term analysis
- Alert System: Configurable thresholds for proactive monitoring
Hardware Compatibility
| Parameter | Specification |
| Model | Serial Cables PCIe Gen6 8-Bay Passive JBOF |
| Firmware Version | 0.0.2 and above |
| Connection Interface | USB Type-C serial interface |
| Serial Configuration | 115200 baud, 8N1 |
| Operating Systems | Windows, Linux, macOS |
| Python Version | 3.7 and above |
2. Installation
System Requirements
- Python 3.7 or higher
- pyserial >= 3.5
- USB Type-C cable for JBOF connection
- Appropriate serial port drivers (usually automatic)
Installation Methods
From PyPI (Recommended)
Install the latest stable version from the Python Package Index:
pip install serialcables-hydra
Verification
Verify your installation by importing the library:
python -c "from serialcables_hydra import JBOFController; print('Installation successful')"
Success: If the import completes without errors, your installation is ready to use.
3. Quick Start Guide
Basic Connection and Monitoring
This example demonstrates the essential operations for connecting to and monitoring your HYDRA JBOF:
from serialcables_hydra import JBOFController, PowerState
# Initialize controller (adjust port for your system)
controller = JBOFController(port='COM3') # Windows
# controller = JBOFController(port='/dev/ttyUSB0') # Linux
# Connect to HYDRA system
if controller.connect():
print("Connected to HYDRA JBOF successfully!")
# Get system information
sys_info = controller.get_system_info()
print(f"Model: {sys_info.model}")
print(f"Firmware: {sys_info.firmware_version}")
print(f"Serial Number: {sys_info.serial_number}")
# Check slot status
slots = controller.show_slot_info()
print("\nSlot Status:")
for slot in slots:
status = "SSD Present" if slot.present else "Empty"
print(f"Slot {slot.slot_number}: {status}")
# Get environmental data
env_data = controller.get_environmental_data()
print(f"\nEnvironmental Status:")
print(f"PSU Voltage: {env_data['voltages']['psu_12v']:.2f}V")
print(f"MCU Temperature: {env_data['temperatures']['mcu']:.1f}°C")
print(f"Fan 1 Speed: {env_data['fan_speeds']['fan_1']} RPM")
# Disconnect when done
controller.disconnect()
else:
print("Failed to connect to HYDRA JBOF")
Essential SSD Management
Control power and monitor individual SSD slots:
from serialcables_hydra import JBOFController, PowerState
import time
controller = JBOFController(port='COM3')
controller.connect()
# Check current power status
power_status = controller.get_slot_power_status()
print("Current slot power status:")
for slot_num, status in power_status.items():
print(f"Slot {slot_num}: {status}")
# Power cycle slot 1 (if SSD is present)
print("\nPower cycling slot 1...")
controller.slot_power(1, PowerState.OFF)
time.sleep(2) # Wait for clean shutdown
controller.slot_power(1, PowerState.ON)
# Control LED for visual indication
controller.control_host_led(1, PowerState.ON) # Turn on LED
time.sleep(3)
controller.control_host_led(1, PowerState.OFF) # Turn off LED
controller.disconnect()
Important: Always ensure SSDs are properly shut down before powering off slots to prevent data corruption. The library provides safe power cycling with appropriate delays.
4. Hardware Setup
Physical Connection
- Power Connection: Ensure the HYDRA JBOF is properly connected to power and powered on
- USB Connection: Connect the USB Type-C cable between your computer and the JBOF's management port
- SSD Installation: Install NVMe SSDs in the desired slots (1-8)
- PCIe Connection: Connect PCIe cables from the host system to the JBOF
Serial Port Identification
Windows
- Open Device Manager
- Expand "Ports (COM & LPT)"
- Look for "USB Serial Port" or similar device
- Note the COM port number (e.g., COM3, COM4)
Linux
# List available serial ports
ls /dev/tty*
# Check for USB serial devices
ls /dev/ttyUSB* /dev/ttyACM*
# View detailed information
dmesg | grep tty
macOS
# List serial ports
ls /dev/tty.*
# Look for USB serial devices
ls /dev/tty.usbserial-* /dev/tty.usbmodem-*
Permissions Setup (Linux/macOS)
Grant access to serial ports:
# Temporary permission
sudo chmod 666 /dev/ttyUSB0
# Permanent solution - add user to dialout group
sudo usermod -a -G dialout $USER
# Log out and back in for changes to take effect
Note: The exact port name may vary depending on your system configuration and connected devices. Always verify the correct port before connecting.
5. API Reference
Core Classes
JBOFController / HydraController
Main controller class for HYDRA JBOF interaction. Both class names are equivalent and can be used interchangeably.
Constructor
JBOFController(port: str, baudrate: int = 115200, timeout: float = 1.0)
| Parameter | Type | Default | Description |
| port | str | Required | Serial port path (e.g., 'COM3', '/dev/ttyUSB0') |
| baudrate | int | 115200 | Serial communication baud rate |
| timeout | float | 1.0 | Command timeout in seconds |
System Control Methods
connect() → bool
Establish serial connection to the JBOF system.
controller = JBOFController('COM3')
if controller.connect():
print("Connection successful")
disconnect()
Close the serial connection and clean up resources.
controller.disconnect()
system_power(state: PowerState) → bool
Control main system power state.
# Power on the system
controller.system_power(PowerState.ON)
# Power off the system
controller.system_power(PowerState.OFF)
reset() → bool
Perform a soft reset of the MCU.
controller.reset()
Slot Management Methods
slot_power(slot: Union[int, str], state: PowerState) → bool
Control power for specific slot(s).
# Power on slot 1
controller.slot_power(1, PowerState.ON)
# Power off slot 3
controller.slot_power(3, PowerState.OFF)
# Power on all slots
controller.slot_power('all', PowerState.ON)
get_slot_power_status() → Dict[int, str]
Retrieve current power status for all slots.
status = controller.get_slot_power_status()
for slot_num, power_state in status.items():
print(f"Slot {slot_num}: {power_state}")
show_slot_info() → List[SlotInfo]
Get detailed information about all slots.
slots = controller.show_slot_info()
for slot in slots:
print(f"Slot {slot.slot_number}:")
print(f" Present: {slot.present}")
print(f" Paddle Card: {slot.paddle_card}")
print(f" Temperature: {slot.temperature}°C")
Environmental Monitoring Methods
get_environmental_data() → Dict
Retrieve comprehensive environmental data.
env_data = controller.get_environmental_data()
# Access temperatures
for location, temp in env_data['temperatures'].items():
print(f"{location}: {temp:.1f}°C")
# Access voltages
for rail, voltage in env_data['voltages'].items():
print(f"{rail}: {voltage:.2f}V")
# Access fan speeds
for fan, rpm in env_data['fan_speeds'].items():
print(f"{fan}: {rpm} RPM")
get_system_info() → SystemInfo
Get complete system information.
sys_info = controller.get_system_info()
print(f"Company: {sys_info.company}")
print(f"Model: {sys_info.model}")
print(f"Serial Number: {sys_info.serial_number}")
print(f"Firmware: {sys_info.firmware_version}")
LED Control Methods
control_host_led(slot: Union[int, str], state: PowerState) → bool
Control host LED on EDSFF drives.
# Turn on host LED for slot 1
controller.control_host_led(1, PowerState.ON)
# Turn off host LED for all slots
controller.control_host_led('all', PowerState.OFF)
control_fault_led(slot: Union[int, str], state: PowerState) → bool
Control fault LED indicators.
# Signal fault condition on slot 2
controller.control_fault_led(2, PowerState.ON)
Fan Control Methods
set_fan_speed(fan_id: int, duty_cycle: int) → bool
Set fan PWM duty cycle for speed control.
# Set fan 1 to 75% speed
controller.set_fan_speed(1, 75)
# Set fan 2 to maximum speed
controller.set_fan_speed(2, 100)
# Set fan 1 to minimum speed
controller.set_fan_speed(1, 30)
Caution: Setting fan speed too low may result in overheating. Monitor temperatures when adjusting fan speeds.
Advanced Features
I2C/SMBus Communication
i2c_read(address: int, slot: int, register: int, length: int) → List[int]
Read data from I2C/SMBus devices connected to SSD slots.
# Read 8 bytes from device 0x50, register 0x00 on slot 1
data = controller.i2c_read(0x50, 1, 0x00, 8)
if data:
hex_data = ' '.join(f'{b:02X}' for b in data)
print(f"Data read: {hex_data}")
i2c_write(address: int, slot: int, data: List[int]) → bool
Write data to I2C/SMBus devices.
# Write data to device 0x50 on slot 1
data = [0x00, 0x01, 0x02, 0x03]
success = controller.i2c_write(0x50, 1, data)
Control Buzzer
from serialcables_hydra import BuzzerState
# Turn buzzer on
controller.control_buzzer(BuzzerState.ON)
# Turn buzzer off
controller.control_buzzer(BuzzerState.OFF)
# Enable buzzer for alerts
controller.control_buzzer(BuzzerState.ENABLE)
Additional Reset Functions
smbus_reset(slot: Union[int, str]) → bool
Send SMBus reset signal to selected slot(s).
controller.smbus_reset(1) # Reset SMBus for slot 1
controller.smbus_reset('all') # Reset SMBus for all slots
ssd_reset(slot: Union[int, str], channel: Optional[str] = None) → bool
Send PERST# reset signal to selected slot(s).
controller.ssd_reset(1) # Reset both channels for slot 1
controller.ssd_reset(2, 'a') # Reset only channel A for slot 2
controller.ssd_reset(3, 'b') # Reset only channel B for slot 3
6. Monitoring and Alerting
Real-time Monitoring
The HYDRA controller provides comprehensive monitoring capabilities for proactive system management.
Continuous Monitoring Loop
from serialcables_hydra import JBOFController, BuzzerState
import time
def continuous_monitor(port, duration=300):
controller = JBOFController(port)
controller.connect()
start_time = time.time()
alert_count = 0
print("Starting continuous monitoring...")
print("Press Ctrl+C to stop")
try:
while time.time() - start_time < duration:
env_data = controller.get_environmental_data()
# Monitor temperatures
for location, temp in env_data['temperatures'].items():
if temp > 55: # Critical temperature
print(f"CRITICAL: High temperature {location}: {temp:.1f}°C")
controller.control_buzzer(BuzzerState.ON)
alert_count += 1
elif temp > 45: # Warning temperature
print(f"WARNING: Elevated temperature {location}: {temp:.1f}°C")
# Monitor voltages
psu_voltage = env_data['voltages']['psu_12v']
if psu_voltage < 11.5 or psu_voltage > 12.5:
print(f"WARNING: PSU voltage out of range: {psu_voltage:.2f}V")
alert_count += 1
# Monitor fan speeds
for fan, rpm in env_data['fan_speeds'].items():
if rpm < 3000:
print(f"ALERT: Low fan speed {fan}: {rpm} RPM")
controller.control_buzzer(BuzzerState.ON)
alert_count += 1
# Display current status every 30 seconds
if int(time.time() - start_time) % 30 == 0:
print(f"Status: {alert_count} alerts, PSU: {psu_voltage:.2f}V")
time.sleep(5) # Check every 5 seconds
except KeyboardInterrupt:
print("\nMonitoring stopped by user")
finally:
controller.control_buzzer(BuzzerState.OFF)
controller.disconnect()
print(f"Monitoring complete. Total alerts: {alert_count}")
# Run monitoring for 5 minutes
continuous_monitor('COM3', duration=300)
Alert Thresholds
| Parameter | Warning Level | Critical Level |
| Temperature | 45°C | 55°C |
| PSU Voltage | < 11.5V or > 12.5V | < 11.0V or > 13.0V |
| Fan Speed | < 3000 RPM | < 1000 RPM |
| Current | > 80% rated | > 95% rated |
Data Logging
Implement data logging for historical analysis and trending:
import csv
import datetime
from serialcables_hydra import JBOFController
def log_environmental_data(port, log_file, interval=60, duration=3600):
controller = JBOFController(port)
controller.connect()
# Create CSV file with headers
with open(log_file, 'w', newline='') as csvfile:
fieldnames = ['timestamp', 'mcu_temp', 'psu_voltage',
'fan1_rpm', 'fan2_rpm', 'slot1_temp', 'slot1_power']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
start_time = time.time()
while time.time() - start_time < duration:
env_data = controller.get_environmental_data()
slots = controller.show_slot_info()
# Log current data
log_entry = {
'timestamp': datetime.datetime.now().isoformat(),
'mcu_temp': env_data['temperatures']['mcu'],
'psu_voltage': env_data['voltages']['psu_12v'],
'fan1_rpm': env_data['fan_speeds']['fan_1'],
'fan2_rpm': env_data['fan_speeds']['fan_2'],
'slot1_temp': slots[0].temperature if slots else 0,
'slot1_power': slots[0].power if slots else 0
}
writer.writerow(log_entry)
csvfile.flush() # Ensure data is written
print(f"Logged data at {log_entry['timestamp']}")
time.sleep(interval)
controller.disconnect()
print(f"Data logging complete. File saved: {log_file}")
# Log data every minute for 1 hour
log_environmental_data('COM3', 'hydra_log.csv', interval=60, duration=3600)
7. Troubleshooting
Connection Issues
Problem: Cannot Connect to Serial Port
# Test port availability
import serial
try:
ser = serial.Serial('COM3', 115200, timeout=1)
print("Port is available")
ser.close()
except serial.SerialException as e:
print(f"Port error: {e}")
Solutions:
- Verify Port Name: Double-check the correct port identifier for your system
- Check Cable Connection: Ensure USB Type-C cable is securely connected
- Driver Installation: Install or update USB-to-serial drivers if needed
- Permission Issues: On Linux/macOS, ensure user has permission to access serial ports
- Port in Use: Close any other applications that might be using the port
Problem: Commands Timeout
Solutions:
- Increase timeout value in constructor: JBOFController('COM3', timeout=5.0)
- Check baud rate matches device configuration (115200)
- Verify firmware compatibility (version 0.0.2+)
Problem: Inconsistent Responses
Solutions:
- Add delays between commands for busy systems
- Implement retry logic for critical operations
- Check for electrical interference near USB cable
Hardware Issues
Problem: SSD Not Detected
Diagnostic Steps:
# Check slot status
slots = controller.show_slot_info()
for slot in slots:
print(f"Slot {slot.slot_number}: Present={slot.present}, Power={slot.power_status}")
# Verify power status
power_status = controller.get_slot_power_status()
print("Power status:", power_status)
# Check clock signals
clock_status = controller.check_clock_input()
print("Clock status:", clock_status)
Solutions:
- Verify SSD is properly seated in slot
- Check power connections to JBOF
- Ensure PCIe cables are connected
- Power cycle the affected slot
Problem: High Temperature Alerts
Diagnostic Steps:
# Check all temperature sensors
env_data = controller.get_environmental_data()
for location, temp in env_data['temperatures'].items():
status = "CRITICAL" if temp > 55 else "WARNING" if temp > 45 else "OK"
print(f"{location}: {temp:.1f}°C [{status}]")
# Check fan operation
for fan, rpm in env_data['fan_speeds'].items():
status = "LOW" if rpm < 3000 else "OK"
print(f"{fan}: {rpm} RPM [{status}]")
Solutions:
- Verify fans are operating at appropriate speeds
- Check for obstructed airflow
- Reduce ambient temperature if possible
- Consider reducing SSD workload temporarily
Software Issues
Problem: Import Errors
# Verify installation
pip show serialcables-hydra
# Check Python path
import sys
print("Python path:", sys.path)
# Test basic import
try:
from serialcables_hydra import JBOFController
print("Import successful")
except ImportError as e:
print(f"Import failed: {e}")
Problem: Permission Denied (Linux/macOS)
# Check current user groups
groups
# Add user to dialout group
sudo usermod -a -G dialout $USER
# Alternative: temporary permission
sudo chmod 666 /dev/ttyUSB0
Diagnostic Tools
System Health Check
def system_health_check(port):
"""Comprehensive system health check"""
try:
controller = JBOFController(port)
if not controller.connect():
print("[FAILED] Connection failed")
return False
print("[SUCCESS] Connection successful")
# Run built-in diagnostics
diag_results = controller.run_diagnostics()
print("\nDiagnostic Results:")
for device, status in diag_results.items():
symbol = "[OK]" if status == "OK" else "[FAIL]"
print(f"{symbol} {device}: {status}")
# Check environmental status
env_data = controller.get_environmental_data()
print("\nEnvironmental Status:")
# Temperature check
temps_ok = all(temp < 50 for temp in env_data['temperatures'].values())
print(f"[{'OK' if temps_ok else 'WARN'}] Temperatures: {'OK' if temps_ok else 'Elevated'}")
# Fan check
fans_ok = all(rpm > 3000 for rpm in env_data['fan_speeds'].values())
print(f"[{'OK' if fans_ok else 'FAIL'}] Fans: {'OK' if fans_ok else 'Low Speed'}")
# Voltage check
voltage = env_data['voltages']['psu_12v']
voltage_ok = 11.5 <= voltage <= 12.5
print(f"[{'OK' if voltage_ok else 'WARN'}] PSU Voltage: {voltage:.2f}V {'OK' if voltage_ok else 'Out of Range'}")
controller.disconnect()
return True
except Exception as e:
print(f"[FAILED] Health check failed: {e}")
return False
# Run health check
system_health_check('COM3')
8. Examples
Complete System Management
This example demonstrates comprehensive system management including monitoring, alerts, and automated responses:
from serialcables_hydra import JBOFController, PowerState, BuzzerState
import time
import logging
# Configure logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
class HydraSystemManager:
def __init__(self, port, temp_warning=45, temp_critical=55):
self.controller = JBOFController(port)
self.temp_warning = temp_warning
self.temp_critical = temp_critical
self.alert_count = 0
def connect(self):
"""Establish connection and perform initial checks"""
if not self.controller.connect():
logger.error("Failed to connect to JBOF")
return False
logger.info("Connected to JBOF successfully")
# Initial system check
sys_info = self.controller.get_system_info()
logger.info(f"System: {sys_info.model} (FW: {sys_info.firmware_version})")
return True
def monitor_system(self, duration=3600):
"""Monitor system for specified duration"""
start_time = time.time()
last_alert_time = 0
logger.info(f"Starting system monitoring for {duration} seconds")
try:
while time.time() - start_time < duration:
env_data = self.controller.get_environmental_data()
current_time = time.time()
# Temperature monitoring
max_temp = max(env_data['temperatures'].values())
if max_temp > self.temp_critical:
self.handle_critical_temperature(max_temp)
elif max_temp > self.temp_warning:
self.handle_temperature_warning(max_temp)
# Fan monitoring
min_fan_speed = min(env_data['fan_speeds'].values())
if min_fan_speed < 1000:
self.handle_fan_failure(min_fan_speed)
elif min_fan_speed < 3000:
self.handle_fan_warning(min_fan_speed)
# Voltage monitoring
voltage = env_data['voltages']['psu_12v']
if not (11.5 <= voltage <= 12.5):
self.handle_voltage_warning(voltage)
# Log status every 5 minutes
if current_time - last_alert_time > 300:
logger.info(f"Status: Temp={max_temp:.1f}°C, Voltage={voltage:.2f}V, "
f"Fans={min_fan_speed}RPM, Alerts={self.alert_count}")
last_alert_time = current_time
time.sleep(30) # Check every 30 seconds
except KeyboardInterrupt:
logger.info("Monitoring stopped by user")
finally:
self.controller.control_buzzer(BuzzerState.OFF)
logger.info(f"Monitoring complete. Total alerts: {self.alert_count}")
def handle_critical_temperature(self, temp):
"""Handle critical temperature condition"""
logger.critical(f"CRITICAL TEMPERATURE: {temp:.1f}°C")
self.controller.control_buzzer(BuzzerState.ON)
self.alert_count += 1
# Increase fan speeds to maximum
self.controller.set_fan_speed(1, 100)
self.controller.set_fan_speed(2, 100)
logger.info("Fan speeds increased to maximum")
def handle_temperature_warning(self, temp):
"""Handle temperature warning"""
logger.warning(f"Temperature warning: {temp:.1f}°C")
self.alert_count += 1
def handle_fan_failure(self, rpm):
"""Handle fan failure"""
logger.critical(f"FAN FAILURE: {rpm} RPM")
self.controller.control_buzzer(BuzzerState.ON)
self.alert_count += 1
def handle_fan_warning(self, rpm):
"""Handle fan speed warning"""
logger.warning(f"Low fan speed: {rpm} RPM")
self.alert_count += 1
def handle_voltage_warning(self, voltage):
"""Handle voltage out of range"""
logger.warning(f"Voltage out of range: {voltage:.2f}V")
self.alert_count += 1
def power_cycle_slot(self, slot_number):
"""Safely power cycle a specific slot"""
logger.info(f"Power cycling slot {slot_number}")
# Turn on indicator LED
self.controller.control_host_led(slot_number, PowerState.ON)
# Power off
self.controller.slot_power(slot_number, PowerState.OFF)
logger.info(f"Slot {slot_number} powered off")
time.sleep(5) # Wait for clean shutdown
# Power on
self.controller.slot_power(slot_number, PowerState.ON)
logger.info(f"Slot {slot_number} powered on")
time.sleep(3)
# Turn off indicator LED
self.controller.control_host_led(slot_number, PowerState.OFF)
def disconnect(self):
"""Clean disconnection"""
if self.controller:
self.controller.disconnect()
logger.info("Disconnected from JBOF")
# Usage example
if __name__ == "__main__":
manager = HydraSystemManager('COM3', temp_warning=45, temp_critical=55)
if manager.connect():
try:
# Monitor system for 1 hour
manager.monitor_system(duration=3600)
finally:
manager.disconnect()
Automated Slot Testing
Automated testing routine for all populated slots:
def automated_slot_test(port):
"""Comprehensive automated slot testing"""
controller = JBOFController(port)
if not controller.connect():
print("Connection failed")
return
print("Starting automated slot testing...")
# Get initial slot status
slots = controller.show_slot_info()
populated_slots = [slot for slot in slots if slot.present]
if not populated_slots:
print("No SSDs detected for testing")
controller.disconnect()
return
print(f"Testing {len(populated_slots)} populated slots")
test_results = []
for slot in populated_slots:
slot_num = slot.slot_number
print(f"\nTesting Slot {slot_num}...")
test_result = {
'slot': slot_num,
'initial_temp': slot.temperature,
'power_cycle_success': False,
'led_test_success': False,
'final_temp': None
}
try:
# Test LED control
print(" Testing LED control...")
controller.control_host_led(slot_num, PowerState.ON)
time.sleep(2)
controller.control_host_led(slot_num, PowerState.OFF)
test_result['led_test_success'] = True
print(" [SUCCESS] LED test passed")
# Test power cycling
print(" Testing power cycle...")
initial_power = controller.get_slot_power_status()[slot_num]
# Power off
controller.slot_power(slot_num, PowerState.OFF)
time.sleep(3)
off_power = controller.get_slot_power_status()[slot_num]
# Power on
controller.slot_power(slot_num, PowerState.ON)
time.sleep(5)
final_power = controller.get_slot_power_status()[slot_num]
if initial_power == 'on' and off_power == 'off' and final_power == 'on':
test_result['power_cycle_success'] = True
print(" [SUCCESS] Power cycle test passed")
else:
print(f" [FAILED] Power cycle test failed: {initial_power}→{off_power}→{final_power}")
# Get final temperature
updated_slots = controller.show_slot_info()
final_slot = next(s for s in updated_slots if s.slot_number == slot_num)
test_result['final_temp'] = final_slot.temperature
except Exception as e:
print(f" ❌ Test failed: {e}")
test_results.append(test_result)
time.sleep(2) # Brief pause between slot tests
# Print summary
print("\n" + "="*50)
print("TEST SUMMARY")
print("="*50)
for result in test_results:
slot_num = result['slot']
led_status = "PASS" if result['led_test_success'] else "FAIL"
power_status = "PASS" if result['power_cycle_success'] else "FAIL"
temp_change = result['final_temp'] - result['initial_temp'] if result['final_temp'] else 0
print(f"Slot {slot_num}:")
print(f" LED Test: {led_status}")
print(f" Power Test: {power_status}")
print(f" Temperature: {result['initial_temp']:.1f}°C → {result['final_temp']:.1f}°C ({temp_change:+.1f}°C)")
print()
# Overall results
total_tests = len(test_results)
led_passes = sum(1 for r in test_results if r['led_test_success'])
power_passes = sum(1 for r in test_results if r['power_cycle_success'])
print(f"Overall Results:")
print(f" LED Tests: {led_passes}/{total_tests} passed")
print(f" Power Tests: {power_passes}/{total_tests} passed")
controller.disconnect()
return test_results
# Run automated testing
results = automated_slot_test('COM3')
9. Command Line Tools
The HYDRA Controller package includes command-line tools for system monitoring and control.
hydra-monitor
Continuous monitoring tool with real-time display and alerting.
Basic Usage
# Basic monitoring
hydra-monitor --port COM3
# Monitor with custom settings
hydra-monitor --port /dev/ttyUSB0 --interval 30 --duration 3600
# Monitor with logging
hydra-monitor --port COM3 --log hydra_data.csv --interval 60
# Monitor with custom alert thresholds
hydra-monitor --port COM3 --temp-warning 50 --temp-critical 60 --fan-min 4000
Command Options
| Option | Description | Default |
| --port | Serial port path | Required |
| --interval | Monitoring interval (seconds) | 30 |
| --duration | Total duration (seconds, 0=infinite) | 0 |
| --log | CSV log file path | None |
| --temp-warning | Temperature warning threshold (°C) | 45 |
| --temp-critical | Temperature critical threshold (°C) | 55 |
| --fan-min | Minimum fan speed (RPM) | 3000 |
| --voltage-min | Minimum voltage (V) | 11.5 |
| --voltage-max | Maximum voltage (V) | 12.5 |
hydra-cli
Command-line interface for direct system control and status queries.
Available Commands
# System status
hydra-cli --port COM3 status
# Power control
hydra-cli --port COM3 power-on --slot 1
hydra-cli --port COM3 power-off --slot 2
hydra-cli --port COM3 power-cycle --slot 3
# Environmental data
hydra-cli --port COM3 environment
hydra-cli --port COM3 temperatures
hydra-cli --port COM3 fans
# LED control
hydra-cli --port COM3 led-on --slot 1
hydra-cli --port COM3 led-off --slot all
# Diagnostics
hydra-cli --port COM3 diagnostics
hydra-cli --port COM3 version
Output Formats
# Default human-readable output
hydra-cli --port COM3 status
# JSON output for scripting
hydra-cli --port COM3 status --format json
# CSV output for data analysis
hydra-cli --port COM3 environment --format csv
Integration with Scripts
Example shell script for automated monitoring:
#!/bin/bash
# HYDRA monitoring script
PORT="/dev/ttyUSB0"
LOG_DIR="/var/log/hydra"
DATE=$(date +%Y%m%d_%H%M%S)
# Create log directory
mkdir -p $LOG_DIR
# Start background monitoring
hydra-monitor --port $PORT --log "$LOG_DIR/hydra_$DATE.csv" --duration 86400 &
MONITOR_PID=$!
echo "HYDRA monitoring started (PID: $MONITOR_PID)"
echo "Log file: $LOG_DIR/hydra_$DATE.csv"
# Function to check system status
check_status() {
hydra-cli --port $PORT status --format json > "$LOG_DIR/status_$DATE.json"
# Check for critical conditions
CRITICAL=$(hydra-cli --port $PORT temperatures | grep -c "CRITICAL")
if [ $CRITICAL -gt 0 ]; then
echo "CRITICAL: High temperature detected!"
# Send alert (email, SMS, etc.)
# mail -s "HYDRA Critical Alert" admin@company.com < "$LOG_DIR/status_$DATE.json"
fi
}
# Check status every 5 minutes
while kill -0 $MONITOR_PID 2>/dev/null; do
check_status
sleep 300
done
echo "Monitoring completed"
10. Support
Getting Help
For technical support and assistance with the Serial Cables HYDRA Controller:
Contact Information
- HYDRA Specific Support: hydra@serialcables.com
- General Technical Support: support@serialcables.com
- Documentation: GitHub repository and PyPI package page
- Bug Reports: GitHub Issues tracker
Before Contacting Support
Please gather the following information to help expedite support:
System Information
from serialcables_hydra import JBOFController
# Collect system information for support
def collect_support_info(port):
info = {
'library_version': '1.0.0',
'python_version': None,
'platform': None,
'jbof_info': None,
'connection_status': False
}
# Python and platform info
import sys, platform
info['python_version'] = sys.version
info['platform'] = platform.platform()
try:
controller = JBOFController(port)
if controller.connect():
info['connection_status'] = True
sys_info = controller.get_system_info()
info['jbof_info'] = {
'model': sys_info.model,
'firmware': sys_info.firmware_version,
'serial': sys_info.serial_number
}
controller.disconnect()
except Exception as e:
info['connection_error'] = str(e)
return info
# Generate support information
support_data = collect_support_info('COM3')
print("Support Information:")
for key, value in support_data.items():
print(f"{key}: {value}")
Information to Include
- Library version and Python version
- Operating system and platform details
- HYDRA JBOF model and firmware version
- Complete error messages and stack traces
- Steps to reproduce the issue
- Expected vs. actual behavior
- Serial port configuration and connection details
Common Support Scenarios
Hardware Compatibility
Verify your hardware is compatible:
- Confirm JBOF model is supported
- Check firmware version (0.0.2 or higher required)
- Verify USB Type-C cable functionality
Performance Issues
For performance-related concerns:
- Monitor system resources during operation
- Check for thermal throttling or overheating
- Verify adequate power supply capacity
- Analyze environmental data trends
Integration Support
For help integrating with your systems:
- Provide details about your application architecture
- Share relevant code snippets
- Describe integration requirements and constraints
Community Resources
- GitHub Repository: Source code, examples, and issues
- PyPI Package Page: Installation and version information
- Documentation: Online documentation and API reference
- Release Notes: Change log and version history
Feedback and Contributions
We welcome feedback and contributions to improve the HYDRA Controller library:
- Report bugs and suggest features via GitHub Issues
- Contribute code improvements through pull requests
- Share usage examples and best practices
- Help improve documentation and examples