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.