This is UPSController.m in view mode; [Download] [Up]
/* Written 8/12/93 by Max Hailperin <max@nic.gac.edu>, Math/CS department,
Gustavus Adolphus College. Public domain, no warranty. Please share
any improvements with me.
Note: this communications protocol is for use with Best Fortress UPSs. */
#import "daemon.h"
#import "UPSController.h"
#import <sys/ioctl.h>
#import <sys/fcntl.h>
#import <dpsclient/dpsclient.h>
#import <appkit/Application.h>
#import <stdio.h>
#import <stdlib.h>
#import <libc.h>
#import <time.h>
#import <ctype.h>
#import <syslog.h>
#ifndef POLLING_PERIOD
#define POLLING_PERIOD 60.0
#endif
#ifndef RETRY_PERIOD
#define RETRY_PERIOD 5.0
#endif
#ifndef RETRIES
#define RETRIES 3
#endif
#ifndef SHUTDOWN_MINUTES
#define SHUTDOWN_MINUTES 5
#endif
#ifndef SHUTDOWN_ALARM_MASK
#define SHUTDOWN_ALARM_MASK 0x292
#endif
#ifndef SHUTDOWN_DELAY
#define SHUTDOWN_DELAY 60
#endif
#ifndef POWERDOWN_DELAY
#define POWERDOWN_DELAY 120
#endif
#define BYTES_IN_F_STRING 40
#define HEX_DIGITS_PER_BYTE 2
#define ctrl(c) ((c)&0x1f)
static struct {
int systemMode, inverterStatus, alarmStatus, ACInputVolts, ACOutputVolts,
ACOutputDeciAmperes, ACLoadVA, batteryDeciVolts, deciHertz, minutes,
ambientTemp, ROMVersion, time;
} status;
static DPSTimedEntry retryTE;
static int retries;
static int checksumOK(const unsigned char *fString){
unsigned char sum=0;
int i;
for(i = 0; i < BYTES_IN_F_STRING; i++)
sum += fString[i];
return sum == 0;
}
static int asDecimal(const unsigned char *bytes, int count){
int value=0;
while(count--){
value *= 100;
value += 10*(*bytes/16) + *bytes % 16;
bytes++;
}
return value;
}
static void describeTrouble(){
if(status.inverterStatus)
syslog(FACILITY|LOG_EMERG,
"Running on battery power; backup time remaining: %d minutes.",
status.minutes);
if(status.alarmStatus & 0x1)
syslog(FACILITY|LOG_EMERG, "UPS memory error");
if(status.alarmStatus & 0x2)
syslog(FACILITY|LOG_EMERG, "Check battery");
if(status.alarmStatus & 0x4)
syslog(FACILITY|LOG_EMERG, "Check inverter");
if(status.alarmStatus & 0x8)
syslog(FACILITY|LOG_EMERG, "Low runtime remaining");
if(status.alarmStatus & 0x10)
syslog(FACILITY|LOG_EMERG, "Low battery");
if(status.alarmStatus & 0x20)
syslog(FACILITY|LOG_EMERG, "Circuit breaker shutdown");
if(status.alarmStatus & 0x40)
syslog(FACILITY|LOG_EMERG, "Overload");
if(status.alarmStatus & 0x80)
syslog(FACILITY|LOG_EMERG, "High temperature");
if(status.alarmStatus & 0x100)
syslog(FACILITY|LOG_EMERG, "Site wiring fault");
if(status.alarmStatus & 0x200)
syslog(FACILITY|LOG_EMERG, "High battery");
}
static void doShutdown(FILE *ups){
syslog(FACILITY|LOG_EMERG,
"Warning!!! System shutdown in %d seconds; please log off NOW!",
SHUTDOWN_DELAY);
#ifndef DEBUG
sleep(SHUTDOWN_DELAY);
fprintf(ups, "off %d\r", POWERDOWN_DELAY);
system("/usr/etc/halt -p");
#endif
}
static void handleTrouble(FILE *ups){
/* should do the filtering of transients like checkups somehow? */
describeTrouble();
if((status.alarmStatus & SHUTDOWN_ALARM_MASK) ||
(status.inverterStatus && status.minutes < SHUTDOWN_MINUTES))
doShutdown(ups);
}
static void processFString(const unsigned char *fString, FILE *ups){
if(!checksumOK(fString))
syslog(FACILITY|LOG_CRIT, "Checksum error in UPS F string");
else{
if(retryTE){
DPSRemoveTimedEntry(retryTE);
retryTE = 0;
}
status.systemMode = fString[5];
status.inverterStatus = fString[8];
status.alarmStatus = fString[11]<<8 + fString[10];
status.ACInputVolts = asDecimal(fString+12, 2);
status.ACOutputVolts = asDecimal(fString+14, 2);
status.ACOutputDeciAmperes = asDecimal(fString+18, 2);
status.ACLoadVA = asDecimal(fString+20, 3);
status.batteryDeciVolts = asDecimal(fString+25, 2);
status.deciHertz = asDecimal(fString+27, 2)/10;
status.minutes = asDecimal(fString+29, 2);
status.ambientTemp = asDecimal(fString+31, 2);
status.ROMVersion = asDecimal(fString+37, 2);
status.time = time(0);
if(status.inverterStatus || status.alarmStatus)
handleTrouble(ups);
}
}
static void reallyRequestFString(FILE *ups){
putc('f', ups);
putc('\r', ups);
}
static void retry(DPSTimedEntry retryTE, double now, void *ups){
#ifdef DEBUG
printf(">>>retrying\n");
#endif
if(retries++ == RETRIES)
syslog(FACILITY|LOG_CRIT, "Not succesfully communicating with UPS");
putc(ctrl('q'), (FILE*)ups);
reallyRequestFString((FILE*)ups);
}
static void requestFString(FILE *ups){
if(!retryTE){
retries = 0;
retryTE= DPSAddTimedEntry(RETRY_PERIOD, retry, ups, NX_RUNMODALTHRESHOLD);
reallyRequestFString(ups);
}
}
static void handleInput(int fd, void* vfp){
static unsigned char bytes[BYTES_IN_F_STRING];
static char hexDigits[HEX_DIGITS_PER_BYTE+1];
static unsigned char *bytePtr=bytes;
static char *hexPtr=hexDigits;
int c;
FILE *fp = (FILE*)vfp;
while((c = getc(fp)) != EOF){
#ifdef DEBUG
putchar(c);
#endif
if(c == '\r'
&& bytePtr-bytes == BYTES_IN_F_STRING
&& hexPtr == hexDigits){
processFString(bytes, fp);
bytePtr = bytes;
} else if(!isxdigit(c)){
if(c == '[' || c == ']' || c == '{' || c == '}')
requestFString(fp);
bytePtr = bytes;
hexPtr = hexDigits;
} else{
*hexPtr++ = c;
if(hexPtr-hexDigits == HEX_DIGITS_PER_BYTE){
if(bytePtr-bytes == BYTES_IN_F_STRING){
syslog(FACILITY|LOG_CRIT, "Too many hex digits in a row from UPS");
bytePtr = bytes;
}
*bytePtr++ = strtol(hexDigits, NULL, 16);
hexPtr = hexDigits;
}
}
}
clearerr(fp);
}
static void poll(DPSTimedEntry pollTE, double now, void *ups){
requestFString((FILE*)ups);
}
@implementation UPSController
-initOn: (const char *)tty
{
struct sgttyb modes = {B1200, B1200, 0, 0, RAW};
FILE *ups;
if((ups = fopen(tty, "r+")) == NULL){
syslog(FACILITY|LOG_CRIT, "Can't open alleged tty %s; exiting.", tty);
exit(1);
}
setbuf(ups, NULL);
ioctl(fileno(ups), TIOCSETP, &modes);
fcntl(fileno(ups), F_SETFL, FNDELAY);
DPSAddFD(fileno(ups), handleInput, ups, NX_MODALRESPTHRESHOLD);
requestFString(ups);
DPSAddTimedEntry(POLLING_PERIOD, poll, ups, NX_RUNMODALTHRESHOLD);
return self;
}
-(int)systemMode:(int*)systemMode inverterStatus:(int*)inverterStatus
alarmStatus:(int*)alarmStatus ACInputVolts:(int*)ACInputVolts
ACOutputVolts:(int*)ACOutputVolts
ACOutputDeciAmperes:(int*)ACOutputDeciAmperes ACLoadVA:(int*)ACLoadVA
batteryDeciVolts:(int*)batteryDeciVolts deciHertz:(int*)deciHertz
minutes:(int*)minutes ambientTemp:(int*)ambientTemp
ROMVersion:(int*)ROMVersion time:(int*)time
{
*systemMode = status.systemMode;
*inverterStatus = status.inverterStatus;
*alarmStatus = status.alarmStatus;
*ACInputVolts = status.ACInputVolts;
*ACOutputVolts = status.ACOutputVolts;
*ACOutputDeciAmperes = status.ACOutputDeciAmperes;
*ACLoadVA = status.ACLoadVA;
*batteryDeciVolts = status.batteryDeciVolts;
*deciHertz = status.deciHertz;
*minutes = status.minutes;
*ambientTemp = status.ambientTemp;
*ROMVersion = status.ROMVersion;
*time = status.time;
return 0;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.