555 lines
13 KiB
PHP
555 lines
13 KiB
PHP
<?php
|
|
|
|
require_once(dirname(__FILE__) . "/../test_framework.php");
|
|
|
|
abstract class PsuCpx400LockStatus {
|
|
const Unknown = -2;
|
|
const Disabled = -1;
|
|
const Unlocked = 0;
|
|
const Locked = 1;
|
|
|
|
private static $enum = array(
|
|
-2 => "Unknown",
|
|
-1 => "Disabled",
|
|
0 => "Unlocked",
|
|
1 => "Locked"
|
|
);
|
|
|
|
public static function toInt($name) {
|
|
return array_search($name, self::$enum);
|
|
}
|
|
|
|
public static function str($int) {
|
|
return self::$enum[$int];
|
|
}
|
|
};
|
|
|
|
class PsuCpx400 {
|
|
private $__componentName = "PsuCpx400";
|
|
private $__requestDelayUs = 80000; // We need some delay after every request because else the PSU will choke
|
|
private $name = "psu_cpx400";
|
|
private $debug = false;
|
|
private $__fd = null;
|
|
|
|
/* Hardware capability */
|
|
private $__capabilities = array(
|
|
"channels" => 2,
|
|
"v_min" => 0.00,
|
|
"v_max" => 60.00,
|
|
"i_min" => 0.01,
|
|
"i_max" => 20.00
|
|
);
|
|
|
|
private $__info = array (
|
|
"host" => "",
|
|
"port" => "",
|
|
"lock" => PsuCpx400LockStatus::Unknown,
|
|
"name" => "",
|
|
"model" => "",
|
|
"serial" => "",
|
|
"version" => ""
|
|
);
|
|
|
|
private $__channelsInfo = array (
|
|
array(
|
|
"id" => 1, // Channel ID
|
|
"enabled" => false, // Output enabled
|
|
"v" => 0.0, // Voltage
|
|
"v_inc" => 0.0, // Voltage increment step
|
|
"v_umin" => 0.0, // Voltage min (user limit)
|
|
"v_umax" => 0.0, // Current max (user limit)
|
|
"i" => 0.0, // Current
|
|
"i_inc" => 0.0, // Current increment step
|
|
"i_umin" => 0.0, // Current min (user limit)
|
|
"i_umax" => 0.0, // Current max (user limit)
|
|
),
|
|
array(
|
|
"id" => 2, // Channel ID
|
|
"enabled" => false, // Output enabled
|
|
"v" => 0.0, // Voltage
|
|
"v_inc" => 0.0, // Voltage increment step
|
|
"v_umin" => 0.0, // Voltage min (user limit)
|
|
"v_umax" => 0.0, // Voltage max (user limit)
|
|
"i" => 0.0, // Current
|
|
"i_max" => 0.0, // Current max
|
|
"i_inc" => 0.0, // Current increment step
|
|
"i_umin" => 0.0, // Current min (user limit)
|
|
"i_umax" => 0.0, // Current max (user limit)
|
|
)
|
|
);
|
|
|
|
private function debug($msg) {
|
|
if ($this->debug) {
|
|
$msg = str_replace("\r", '\r', $msg);
|
|
$msg = str_replace("\n", '\n', $msg);
|
|
DiTestMsg::dbg(trim($msg), $this->__componentName);
|
|
}
|
|
}
|
|
|
|
private function err($msg) {
|
|
DiTestMsg::err(trim($msg), $this->__componentName);
|
|
}
|
|
|
|
private function warn($msg) {
|
|
DiTestMsg::warn(trim($msg), $this->__componentName);
|
|
}
|
|
|
|
private function fail($msg) {
|
|
DiTestMsg::fail(trim($msg), $this->__componentName);
|
|
}
|
|
|
|
private function pass($msg) {
|
|
DiTestMsg::pass(trim($msg), $this->__componentName);
|
|
}
|
|
|
|
function __construct($host = "psu", $port = 9221, $debug = false) {
|
|
$this->debug = $debug;
|
|
$this->__fd = fsockopen($host, $port, $errno, $errstr, 30);
|
|
if ($this->__fd === FALSE) {
|
|
$this->fail("$errstr ($errno)");
|
|
} else {
|
|
$this->setInfo("host", $host);
|
|
$this->setInfo("port", $port);
|
|
|
|
$this->requestInfo();
|
|
$this->requestLockStatus();
|
|
|
|
if ($this->getInfo("lock") != PsuCpx400LockStatus::Unlocked) {
|
|
$this->warn("PSU not unlocked, ignoring...");
|
|
}
|
|
|
|
$this->outputsOff();
|
|
$this->reset();
|
|
$this->resetLimits();
|
|
}
|
|
}
|
|
|
|
function __destruct() {
|
|
if ($this->__fd) {
|
|
$this->resetLimits();
|
|
$this->outputsOff();
|
|
$this->reset();
|
|
fclose($this->__fd);
|
|
$this->__fd = null;
|
|
}
|
|
}
|
|
|
|
function setInfo($key, $value) {
|
|
$this->__info[$key] = $value;
|
|
}
|
|
|
|
function getInfo($key) {
|
|
return $this->__info[$key];
|
|
}
|
|
|
|
function getCapability($id) {
|
|
return $this->__capabilities[$id];
|
|
}
|
|
|
|
function setChannelInfo($channel, $key, $value) {
|
|
$channel = (int)$channel - 1;
|
|
$this->__channelsInfo[$channel][$key] = $value;
|
|
$this->debug("[setChannelInfo] channel: $channel, \"$key\" => \"$value\"");
|
|
}
|
|
|
|
function getChannelInfo($channel, $key) {
|
|
$channel = (int)$channel - 1;
|
|
$value = $this->__channelsInfo[(int)$channel][$key];
|
|
|
|
if ($key == "enabled") {
|
|
if ($value == true)
|
|
$value = "ON";
|
|
else
|
|
$value = "OFF";
|
|
}
|
|
|
|
$this->debug("[getChannelInfo] channel: $channel, \"$key\" => \"$value\"");
|
|
|
|
return $value;
|
|
}
|
|
|
|
private function reply() {
|
|
if ($this->__fd) {
|
|
$reply = fgets($this->__fd);
|
|
$this->debug("[reply] \"$reply\"");
|
|
return $reply;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
private function request($cmd) {
|
|
if ($this->__fd) {
|
|
$cmd .= "\n";
|
|
$this->debug("[request] cmd: \"$cmd\"");
|
|
fwrite($this->__fd, "$cmd");
|
|
}
|
|
}
|
|
|
|
private function requestInfo() {
|
|
$this->request("*IDN?");
|
|
$r = $this->reply();
|
|
if (!empty($r)) {
|
|
$info = explode(",", $r);
|
|
if (count($info) == 4) {
|
|
$this->setInfo("name", trim($info[0]));
|
|
$this->setInfo("model", trim($info[1]));
|
|
$this->setInfo("serial", trim($info[2]));
|
|
$this->setInfo("version", trim($info[3]));
|
|
} else {
|
|
// TODO something unexpected returned
|
|
}
|
|
}
|
|
}
|
|
|
|
private function requestLockStatus() {
|
|
$this->request("IFLOCK?");
|
|
$r = $this->reply();
|
|
if (!empty($r)) {
|
|
$this->setInfo("lock", (int)trim($r));
|
|
} else {
|
|
$this->setInfo("lock", PsuCpx400LockStatus::Unknown);
|
|
}
|
|
}
|
|
|
|
private function isValidChannel($channel) {
|
|
if ($channel > 0 && $channel <= 2)
|
|
return true;
|
|
|
|
$this->fail("Invalid channel $channel selected");
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Verify if requested channel voltage is within user and hardware limits
|
|
*/
|
|
private function isValidVoltage($channel, $voltage) {
|
|
// Check channel number
|
|
if (!($this->isValidChannel($channel)))
|
|
return false;
|
|
|
|
// User limit
|
|
$min = $this->getChannelInfo($channel, "v_umin");
|
|
$max = $this->getChannelInfo($channel, "v_umax");
|
|
|
|
if ($voltage >= $min && $voltage <= $max) {
|
|
$this->debug("valid voltage for channel: $channel, $voltage");
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
// Hardware limit
|
|
$min = $this->getCapability('v_min');
|
|
$max = $this->getCapability('v_max');
|
|
|
|
if($voltage >= $min && $voltage <= $max) {
|
|
$this->debug("invalid voltage for channel: $channel, $voltage");
|
|
return true;
|
|
}
|
|
*/
|
|
|
|
$this->err("Voltage requested is invalid: $voltage >= $min (min), <= $max (max)");
|
|
return false;
|
|
}
|
|
|
|
public function lock() {
|
|
$ret = false;
|
|
$this->request("IFLOCK");
|
|
$r = $this->reply();
|
|
$this->debug("[lock] " . $r);
|
|
if (!empty($r)) {
|
|
(int)$r;
|
|
if ($r == 1) {
|
|
$ret = true;
|
|
$this->setInfo("lock", PsuCpx400LockStatus::Locked);
|
|
}
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
public function unlock() {
|
|
$ret = false;
|
|
|
|
$this->request("IFUNLOCK");
|
|
$r = $this->reply();
|
|
$this->debug("[unlock] " . $r);
|
|
if (!empty($r)) {
|
|
(int)$r;
|
|
if ($r == 0) {
|
|
$ret = true;
|
|
$this->setInfo("lock", PsuCpx400LockStatus::Unlocked);
|
|
}
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
public function isLocked() {
|
|
$this->requestLockStatus();
|
|
$lock = $this->getInfo("lock");
|
|
if ($lock == PsuCpx400LockStatus::Unlocked)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
public function local() {
|
|
$this->request("LOCAL");
|
|
return true;
|
|
}
|
|
|
|
/* @return true when OK, false otherwise */
|
|
public function setVoltage($channel, $value, $retries = 5, $timeout_ms = 100) {
|
|
$channel = (int)$channel;
|
|
$value = (float)$value;
|
|
|
|
if (!($this->isValidVoltage($channel, $value)))
|
|
return false;
|
|
|
|
$v = 0.00;
|
|
$this->request("V".$channel." $value");
|
|
usleep($this->__requestDelayUs);
|
|
for ($i = 0; $i < $retries; $i++) {
|
|
$v = $this->getVoltage($channel);
|
|
if ($v == $value) {
|
|
$this->debug("[setVoltage] Requested voltage correctly set: \"$v\"");
|
|
return true;
|
|
}
|
|
|
|
$this->debug("[setVoltage] Verify try $i, volt: \"$v\"");
|
|
usleep(1000 * $timeout_ms);
|
|
}
|
|
|
|
$this->fail("[setVoltage] Requested voltage not correctly set to \"$value\": \"$v\"");
|
|
return false;
|
|
}
|
|
|
|
/* Set current for all outputs */
|
|
public function setVoltageAll($value) {
|
|
for ($i = 1; $i < 3; $i++) {
|
|
$this->setVoltage($i, $value);
|
|
}
|
|
}
|
|
|
|
public function getVoltage($channel) {
|
|
(int)$channel = $channel;
|
|
// TODO check channel
|
|
$this->request("V$channel?");
|
|
$v = $this->reply();
|
|
if (!empty($v)) {
|
|
$v = explode(" ", $v);
|
|
$v = (float)$v[1];
|
|
$this->debug("[getVoltage] (channel: " . $channel . "): " . $v);
|
|
$this->setChannelInfo($channel, "v", $v);
|
|
return $v;
|
|
}
|
|
$this->fail("[getVoltage] Unexpected reply \"$v\"");
|
|
return 0;
|
|
}
|
|
|
|
public function incVoltage($channel, $verify = false) {
|
|
$channel = (int)$channel;
|
|
|
|
if (!($this->isValidChannel($channel)))
|
|
return;
|
|
|
|
// TODO check PSU voltage after increment...
|
|
|
|
if (!$verify)
|
|
$this->request("INCV".$channel);
|
|
else
|
|
$this->request("INCV".$channel."V");
|
|
|
|
usleep($this->__requestDelayUs);
|
|
$this->getVoltage($channel);
|
|
}
|
|
|
|
public function getIncVoltage($channel) {
|
|
(int)$channel = $channel;
|
|
// TODO check channel
|
|
$this->request("DELTAV$channel?");
|
|
$v = $this->reply();
|
|
if (!empty($v)) {
|
|
$v = explode(" ", $v);
|
|
$v = (float)$v[1];
|
|
$this->debug("[getIncVoltage] (channel: " . $channel . "):" . $v);
|
|
$this->setChannelInfo($channel, "v_inc", $v);
|
|
return $v;
|
|
}
|
|
$this->fail("[getIncVoltage] Unexpected reply \"$v\"");
|
|
return 0;
|
|
}
|
|
|
|
public function setIncVoltage($channel, $step) {
|
|
$channel = (int)$channel;
|
|
$step = (float)$step;
|
|
if ($step == 0.00) {
|
|
$this->warn("[setIncVoltage] Step of 0.00V is invalid, setting to minimal 0.01V");
|
|
$step = 0.01;
|
|
}
|
|
$this->request("DELTAV".$channel." $step");
|
|
$this->getIncVoltage($channel);
|
|
}
|
|
|
|
public function setMinVoltage($channel, $value) {
|
|
$this->setChannelInfo($channel, "v_umin", $value);
|
|
}
|
|
|
|
public function setMaxVoltage($channel, $value) {
|
|
$this->setChannelInfo($channel, "v_umax", $value);
|
|
}
|
|
|
|
public function getMinVoltage($channel) {
|
|
return $this->getChannelInfo($channel, "v_umin");
|
|
}
|
|
|
|
public function getMaxVoltage($channel) {
|
|
return $this->getChannelInfo($channel, "v_umax");
|
|
}
|
|
|
|
public function setCurrent($channel, $value) {
|
|
$channel = (int)$channel;
|
|
$value = (float)$value;
|
|
$this->request("I$channel $value");
|
|
$this->getCurrent($channel);
|
|
return true;
|
|
}
|
|
|
|
/* Set current for all outputs */
|
|
public function setCurrentAll($value) {
|
|
for ($i = 1; $i < 3; $i++) {
|
|
$this->setCurrent($i, $value);
|
|
}
|
|
}
|
|
|
|
public function getCurrent($channel) {
|
|
(int)$channel = $channel;
|
|
$this->request("I$channel?");
|
|
$c = $this->reply();
|
|
$this->debug("[getCurrent] reply: " . $c);
|
|
if (!empty($c)) {
|
|
$c = explode(" ", $c);
|
|
$c = (float)$c[1];
|
|
$this->debug("[getCurrent] (channel: " . $channel . "):" . $c);
|
|
$this->setChannelInfo($channel, "i", $c);
|
|
return $c;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public function incCurrent($channel) {
|
|
$channel = (int)$channel;
|
|
$this->request("INCI".$channel);
|
|
$this->getCurrent($channel);
|
|
}
|
|
|
|
public function getIncCurrent($channel) {
|
|
(int)$channel = $channel;
|
|
$this->request("DELTAI$channel?");
|
|
$v = $this->reply();
|
|
if (!empty($v)) {
|
|
$v = explode(" ", $v);
|
|
$v = (float)$v[1];
|
|
$this->debug("[getIncCurrent] (channel: " . $channel . "):" . $v);
|
|
$this->setChannelInfo($channel, "i_inc", $v);
|
|
|
|
return $v;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public function setIncCurrent($channel, $step) {
|
|
$channel = (int)$channel;
|
|
$step = (float)$step;
|
|
if ($step == 0.00) {
|
|
$this->warn("[setIncCurrent] Step of 0.00A is invalid, setting to minimal value of 0.01A");
|
|
$step = 0.01;
|
|
}
|
|
$this->request("DELTAI".$channel." $step");
|
|
$this->getIncCurrent($channel);
|
|
}
|
|
|
|
public function setMinCurrent($channel, $value) {
|
|
$this->setChannelInfo($channel, "i_umin", $value);
|
|
}
|
|
|
|
public function setMaxCurrent($channel, $value) {
|
|
$this->setChannelInfo($channel, "i_umax", $value);
|
|
}
|
|
|
|
public function getMinCurrent($channel) {
|
|
return $this->getChannelInfo($channel, "i_umin");
|
|
}
|
|
|
|
public function getMaxCurrent($channel) {
|
|
return $this->getChannelInfo($channel, "i_umax");
|
|
}
|
|
|
|
public function outputOn($channel) {
|
|
$channel = (int)$channel;
|
|
$this->request("OP$channel 1");
|
|
}
|
|
|
|
public function outputOff($channel) {
|
|
$channel = (int)$channel;
|
|
// TODO check channel id
|
|
$this->request("OP$channel 0");
|
|
}
|
|
|
|
/* Enable all outputs */
|
|
public function outputsOn() {
|
|
$this->request("OPALL 1");
|
|
}
|
|
|
|
/* Disable all outputs */
|
|
public function outputsOff() {
|
|
$this->request("OPALL 0");
|
|
}
|
|
|
|
/** Disable all outputs, set all voltage and current to zero */
|
|
public function reset() {
|
|
$this->outputsOff();
|
|
$this->resetLimits();
|
|
|
|
for ($i = 1; $i < 3; $i++) {
|
|
$this->setVoltage($i, 0.00);
|
|
$this->setCurrent($i, 0.01);
|
|
$this->setIncVoltage($i, 0.01);
|
|
$this->setIncCurrent($i, 0.01);
|
|
}
|
|
}
|
|
|
|
public function resetLimits() {
|
|
for ($i = 1; $i < 3; $i++) {
|
|
$this->setMinVoltage($i, $this->getCapability("v_min"));
|
|
$this->setMaxVoltage($i, $this->getCapability("v_max"));
|
|
$this->setMinCurrent($i, $this->getCapability("i_min"));
|
|
$this->setMaxCurrent($i, $this->getCapability("i_max"));
|
|
$this->setIncVoltage($i, 0.01);
|
|
$this->setIncCurrent($i, 0.01);
|
|
}
|
|
}
|
|
|
|
public function info() {
|
|
$info = sprintf(
|
|
"===
|
|
PSU CPX400 Status
|
|
> Host: tcp://%s:%d
|
|
> Serial: %s Version: %s
|
|
> Lock: %s
|
|
> Channel 1: [%s] %.02f V %.02f I (%0.2f Vstep, %0.2f IStep)
|
|
> Channel 2: [%s] %.02f V %.02f I (%0.2f Vstep, %0.2f IStep)
|
|
===\n",
|
|
$this->getInfo("host"), $this->getInfo("port"),
|
|
$this->getInfo("serial"), $this->getInfo("version"),
|
|
PsuCpx400LockStatus::str($this->getInfo("lock")),
|
|
$this->getChannelInfo(1, "enabled"),
|
|
$this->getChannelInfo(1, "v"), $this->getChannelInfo(1, "i"),
|
|
$this->getChannelInfo(1, "v_inc"), $this->getChannelInfo(1, "i_inc"),
|
|
$this->getChannelInfo(2, "enabled"),
|
|
$this->getChannelInfo(2, "v"), $this->getChannelInfo(2, "i"),
|
|
$this->getChannelInfo(2, "v_inc"), $this->getChannelInfo(2, "i_inc")
|
|
);
|
|
return $info;
|
|
}
|
|
}
|