My code (below) is working if I use QoS 0. But for QoS 1 or QoS 2. MQTTClient_publishMessage(...) failed.
Am I missing any configuration? Or, is it because I am using free XIvely account?
I use Paho API.
------------------------------- start of cut -------------------------------------
/**
* #file
*
* Paho MQ Client API to Xively mqtt broker
*
*/
enter code here
#include "MQTTClient.h"
#include <stdlib.h>
void usage()
{
printf("Usage: speicify QoS\n");
printf(" turn on bulb -> ka_pub Qos 1 1\n");
printf(" turn off bulb -> ka_pub Qos 1 0\n");
printf(" send 7 to led -> ka_pub Qos 2 7\n");
printf(" send 2 to led -> ka_pub Qos 2 2\n");
}
int main(int argc, char** argv)
{
int rc = 0;
if (argc < 3) {
usage();
exit (0);
}
char TOPIC[250] ; // given enough space first to avoid malloc
MQTTClient client;
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
MQTTClient_message pubmsg = MQTTClient_message_initializer;
MQTTClient_deliveryToken token;
int QoS = atoi(argv[1]);
if (argv[2][0] == '1') {
strcpy(TOPIC, BULB_TOPIC);
conn_opts.username = BULB_API_KEY ;
conn_opts.password = "" ; // will be ignored
} else
if (argv[2][0] == '2') {
strcpy(TOPIC, LED_TOPIC) ;
conn_opts.username = LED_API_KEY ;
conn_opts.password = "" ; // will be ignored
} else {
printf("Bad arg\n");
usage();
exit (0);
}
setenv("MQTT_C_CLIENT_TRACE", "ON", 1); // same as 'stdout'
setenv("MQTT_C_CLIENT_TRACE_LEVEL", "ERROR", 1); //ERROR, PROTOCOL, MINIMUM, MEDIUM and MAXIMUM
MQTTClient_create(&client, XIVELY_END_URL, "test", MQTTCLIENT_PERSISTENCE_DEFAULT, NULL);
//conn_opts.keepAliveInterval = 20; // init to 60
//conn_opts.cleansession = 1; // default 1, will clean previous msg in server
conn_opts.reliable = 0 ; //default 1, only 1 can in-flight, 0 - allow 10 msg
if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
{
printf("Failed to connect, return code %d\n", rc);
exit(-1);
}
// prepare publish msg
char tmsg[250] ; // check max 250 boundary later
if (argv[2][0] == '1')
sprintf(tmsg, "{\"id\":\"switch\",\"current_value\":\"%c\"}", argv[3][0]);
else
sprintf(tmsg, "{\"id\":\"num\",\"current_value\":\"%c\"}", argv[3][0]) ;
int tmsg_len = strlen(tmsg);
pubmsg.payload = &tmsg[0] ;
pubmsg.payloadlen = tmsg_len; //mlen
pubmsg.qos = QoS;
pubmsg.retained = 0;
MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token);
//MQTTClient_publish(client, TOPIC, pubmsg.payloadlen, pubmsg.payload,
// pubmsg.qos, pubmsg.retained, &token);
rc = MQTTClient_waitForCompletion(client, token, 100000);
printf("Finish publish for TOPIC: %s, QoS: %d, msg of '%s'\n", TOPIC, pubmsg.qos, (char *) pubmsg.payload);
if (rc == 0)
MyLog(LOGA_INFO, "verdict pass");
else
MyLog(LOGA_INFO, "verdict fail");
MQTTClient_disconnect(client, 10000);
MQTTClient_destroy(&client);
return rc;
}
------------------------------- end of cut -------------------------------------
I don't believe Xively supports QoS>0.
Related
It takes up to 40 min before a packet is lost, (at rate of 1 packet every few minutes),
The MCU use Linux kernel 3.18.48,
Using Scope, (on UART's Rx Pin), I can see the packets, (about 15 bytes long), are sent well.
But the read() doesn't return, with any of the packet's bytes,
(VMIN = 1, VTIME = 0, configured to return if at least 1 byte is in the Rx buffer),
This code is used in 4 other projects, with different HW Board, and we never saw this issue before.
Can you share ideas of how to tackle such issue?
How can I debug the UART driver?
To better understand where the packet got lost,
Thanks,
Logic Analyzer of the Lost Packet
E_UARTDRV_STATUS UartDrv_Open(void *pUart, S_UartDrv_InitData *init_data)
{
struct termios tty;
struct serial_struct serial;
/*
* O_RDWR - Opens the port for reading and writing
* O_NOCTTY - The port never becomes the controlling terminal of the process.
* O_NDELAY - Use non-blocking I/O.
* On some systems this also means the RS232 DCD signal line is ignored.
* Note well: if present, the O_EXCL flag is silently ignored by the kernel when opening a serial device like a modem.
* On modern Linux systems programs like ModemManager will sometimes read and write to your device and possibly corrupt your program state.
* To avoid problems with programs like ModemManager you should set TIOCEXCL on the terminal after associating a terminal with the device.
* You cannot open with O_EXCL because it is silently ignored.
*/
fd = open(init_data->PortName, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) // if there is an invalid descriptor, print the reason {
SYS_LOG_ERR_V("fd invalid whilst trying to open com port %s: %s\n", init_data->PortName, strerror(errno));
return UARTDRV_STATUS_ERROR;
}
if (tcflush(fd, TCIOFLUSH) < 0) {
SYS_LOG_ERR_V("Error failed to flush input output buffers %s\n", strerror(errno));
return UARTDRV_STATUS_ERROR;
}
// Enable low latency...this should affect the file /sys/bus/usb-serial/devices/ttyUSB0/latency_timer
if (ioctl(fd, TIOCGSERIAL, &serial) < 0) {
SYS_LOG_ERR_V("Error failed to get latency current value: %s\n", strerror(errno));
return UARTDRV_STATUS_ERROR;
}
serial.flags |= ASYNC_LOW_LATENCY;
if (ioctl(fd, TIOCSSERIAL, &serial) < 0) {
SYS_LOG_ERR_V("Error failed to set Low latency: %s\n", strerror(errno));
return UARTDRV_STATUS_ERROR;
}
if (fcntl(fd, F_SETFL, 0) < 0) {
SYS_LOG_ERR_V("Error failed to set file flags: %s\n", strerror(errno));
return UARTDRV_STATUS_ERROR;
}
/* Get current configuration */
if (tcgetattr(fd, &tty) < 0) {
SYS_LOG_ERR_V("Error failed to get current configuration: %s\n", strerror(errno));
return UARTDRV_STATUS_ERROR;
}
if (cfsetospeed(&tty, init_data->baud) < 0) {
SYS_LOG_ERR_V("Error failed to set output baud rate: %s\n", strerror(errno));
return UARTDRV_STATUS_ERROR;
}
if (cfsetispeed(&tty, init_data->baud) < 0) {
SYS_LOG_ERR_V("Error failed to set input baud rate: %s\n", strerror(errno));
return UARTDRV_STATUS_ERROR;
}
tty.c_cflag |= (CLOCAL | CREAD); /* Enable the receiver and set local mode */
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; /* 8-bit characters */
tty.c_cflag &= ~PARENB; /* no parity bit */
tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
/*
* Input flags - Turn off input processing
* convert break to null byte, no CR to NL translation,
* no NL to CR translation, don't mark parity errors or breaks
* no input parity check, don't strip high bit off,
* no XON/XOFF software flow control
* BRKINT - If this bit is set and IGNBRK is not set, a break condition clears the terminal input and output queues and raises a SIGINT signal for the foreground process group associated with the terminal.
* If neither BRKINT nor IGNBRK are set, a break condition is passed to the application as a single '\0' character if PARMRK is not set, or otherwise as a three-character sequence '\377', '\0', '\0'.
* INPCK - If this bit is set, input parity checking is enabled. If it is not set, no checking at all is done for parity errors on input; the characters are simply passed through to the application.
* Parity checking on input processing is independent of whether parity detection and generation on the underlying terminal hardware is enabled; see Control Modes.
* For example, you could clear the INPCK input mode flag and set the PARENB control mode flag to ignore parity errors on input, but still generate parity on output.
* If this bit is set, what happens when a parity error is detected depends on whether the IGNPAR or PARMRK bits are set. If neither of these bits are set, a byte with a parity error is passed to the application as a '\0' character.
*/
tty.c_iflag &= ~(BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
/*
* IGNBRK - If this bit is set, break conditions are ignored.
* A break condition is defined in the context of asynchronous serial data transmission as a series of zero-value bits longer than a single byte.
*/
tty.c_iflag |= IGNBRK;
/*
* No line processing
* echo off, echo newline off, canonical mode off,
* extended input processing off, signal chars off
*/
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
/*
* Output flags - Turn off output processing
* no CR to NL translation, no NL to CR-NL translation,
* no NL to CR translation, no column 0 CR suppression,
* no Ctrl-D suppression, no fill characters, no case mapping,
* no local output processing
*
* c_oflag &= ~(OCRNL | ONLCR | ONLRET | ONOCR | ONOEOT| OFILL | OLCUC | OPOST);
*/
tty.c_oflag = 0;
/* fetch bytes as they become available */
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 1; // timeout in 10th of second
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
SYS_LOG_ERR_V("Error failed to set new configuration: %s\n", strerror(errno));
return UARTDRV_STATUS_ERROR;
}
uartPeripheral.fd = fd;
return UARTDRV_STATUS_SUCCESS;
}
uint8_t *UartDrv_Rx(S_UartDrv_Handle *handle, uint16_t bytesToRead, uint16_t *numBytesRead)
{
ssize_t n_read;
uint16_t n_TotalReadBytes = 0;
struct timespec timestamp;
struct timespec now;
long diff_ms;
bool Timeout = false, isPartialRead = false;
if (handle == NULL) {
SYS_LOG_ERR("UartDrv Error: async rx error - peripheral error");
exit(EXIT_FAILURE);
}
if (bytesToRead > sizeof(uartPeripheral.buffer_rx)) {
ESILOG_ERR_V("Param Error: Invalid length %u, max length %zu", bytesToRead, sizeof(uartPeripheral.buffer_rx));
*numBytesRead = 0;
return NULL;
}
while(n_TotalReadBytes < bytesToRead && !Timeout) {
do {
n_read = read(uartPeripheral.fd, &uartPeripheral.buffer_rx[n_TotalReadBytes], bytesToRead - n_TotalReadBytes);
if (isPartialRead) {
clock_gettime(CLOCK_REALTIME, &now);
diff_ms = (now.tv_sec - timestamp.tv_sec)*1000;
diff_ms += (now.tv_nsec - timestamp.tv_nsec)/1000000;
if (diff_ms > UART_READ_TIMEOUT_MS) {
SYS_LOG_ERR("UartDrv_Rx: Error, timeout while reading\r\n");
Timeout = true;
}
}
} while ((n_read != -1) && (n_read == 0) && !Timeout);
if (n_read == -1) {
if (errno == EINTR) {
ESILOG_WARN("Uart Interrupted");
continue;
}
ESILOG_ERR_V("Uart Error: [%d, %s]", errno, strerror(errno));
exit(EXIT_FAILURE);
}
n_TotalReadBytes += (uint16_t)n_read;
if (n_TotalReadBytes < bytesToRead) {
//SYS_LOG_DBG_V("UartDrv_Rx: couldn't fetch all bytes, read %hu, expected %hu, continue reading %s\r\n", n_TotalReadBytes, bytesToRead, isPartialRead? "During Partial read": "");
if (!isPartialRead) {
isPartialRead = true;
clock_gettime(CLOCK_REALTIME, ×tamp);
}
}
}
*numBytesRead = n_TotalReadBytes;
return uartPeripheral.buffer_rx;
}
To simplify a function with many terms, a program would be useful that searches for patterns in a file and arranges them in a ranking list. I can imagine that this is an elaborate process, but I'm sure there are people who have built something like this.
An example of a text:
sin(t1)*cos(t1)*t1+t1-sin(t1)*sin(t1-pi)
This should give me such an output like this (min. 2 letters):
6x: "t1"
4x: "(t1"
3x: "n(t1"
3x: "sin"
3x: "sin("
2x: "sin(t1)"
etc.
Does this problem have a name (which I don't know)? Is there a known algorithm that could solve the problem for me?
I have written a small program with QT, which fulfills the task. The approach is to try everything. To solve my problem, it will probably take a few days, because the text files are very large.
If I have the following text as input ("text.txt"):
sin(t1)*cos(t1)*t1+t1-sin(t1)*sin(t1-pi)
I have with the parameters: length 2-5, minimum occurrence: 3
following result:
t1 6
(t 4
(t1 4
si 3
in 3
n( 3
1) 3
)* 3
sin 3
in( 3
n(t 3
t1) 3
1)* 3
sin( 3
in(t 3
n(t1 3
(t1) 3
t1)* 3
sin(t 3
in(t1 3
(t1)* 3
Code:
#include <QCoreApplication>
#include <qdebug.h>
#include <qstring.h>
#include <qfile.h>
#include <qtextstream.h>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QString * wholefile = new QString;
uint64_t minchar = 2;
uint64_t maxchar = 5;
uint64_t min_occur = 3;
QFile file("text.txt");
if(!file.open(QIODevice::ReadOnly)) {
qDebug()<<"error reading file";
}
QTextStream in(&file);
while(!in.atEnd()) {
QString line = in.readLine();
wholefile->append(line);
}
file.close();
QStringList * allpatterns = new QStringList;
for(uint64_t i=minchar; i<=maxchar;i++){
for(uint64_t pos=0; pos<wholefile->length()-i;pos++){
QString pattern = wholefile->mid(pos,i);
if(allpatterns->contains(pattern)==0){
allpatterns->append(pattern);
}
}
}
uint64_t * strcnt = new uint64_t[allpatterns->length()];
uint64_t maximum_cnt = 0;
QStringList * interestingpatterns = new QStringList;
uint64_t nr_of_patterns = 0;
for(uint64_t i=0; i<allpatterns->length();i++){
QString str = allpatterns->at(i);
strcnt[nr_of_patterns] = wholefile->count(str);
if(strcnt[nr_of_patterns]>=min_occur){
if(strcnt[nr_of_patterns]>maximum_cnt){
maximum_cnt = strcnt[nr_of_patterns];
}
interestingpatterns->append(str);
nr_of_patterns++;
}
}
/* display result*/
QFile file2("out.txt");
if (!file2.open(QIODevice::WriteOnly | QIODevice::Text))
qDebug()<<"error writing file";
QTextStream out(&file2);
uint64_t current_max = maximum_cnt;
while(current_max>=min_occur){
for(uint64_t i=0; i<interestingpatterns->length();i++){
if(strcnt[i]==current_max){
QString str = interestingpatterns->at(i);
qDebug()<<str<<strcnt[i];
out <<str<<" "<< QString::number(strcnt[i])<<"\n";
}
}
current_max--;
}
file2.close();
return a.exec();
}
The printf function calls write (re. forktest.c):
void printf ( int fd, char *s, ... )
{
write( fd, s, strlen(s) );
}
Passing 1 as the fd writes to the console (as 1 maps to stdout). But where is write defined? I only see its declaration in user.h.
int write ( int, void*, int );
I'm assuming it somehow gets redirected to filewrite in file.c.
int filewrite (struct file *f, char *addr, int n )
{
int r;
if ( f->writable == 0 )
return -1;
if ( f->type == FD_PIPE )
return pipewrite( f->pipe, addr, n );
if ( f->type == FD_INODE )
{
// write a few blocks at a time to avoid exceeding
// the maximum log transaction size, including
// i-node, indirect block, allocation blocks,
// and 2 blocks of slop for non-aligned writes.
// this really belongs lower down, since writei()
// might be writing a device like the console.
int max = ( ( MAXOPBLOCKS - 1 - 1 - 2 ) / 2 ) * 512;
int i = 0;
while ( i < n )
{
int n1 = n - i;
if ( n1 > max )
n1 = max;
begin_op();
ilock( f->ip );
if ( ( r = writei( f->ip, addr + i, f->off, n1 ) ) > 0 )
f->off += r;
iunlock( f->ip );
end_op();
if ( r < 0 )
break;
if ( r != n1 )
panic( "short filewrite" );
i += r;
}
return i == n ? n : -1;
}
panic( "filewrite" );
}
And filewrite calls writei which is defined in fs.c.
int writei ( struct inode *ip, char *src, uint off, uint n )
{
uint tot, m;
struct buf *bp;
if ( ip->type == T_DEV )
{
if ( ip->major < 0 || ip->major >= NDEV || !devsw[ ip->major ].write )
return -1;
return devsw[ ip->major ].write( ip, src, n );
}
if ( off > ip->size || off + n < off )
return -1;
if ( off + n > MAXFILE*BSIZE )
return -1;
for ( tot = 0; tot < n; tot += m, off += m, src += m )
{
bp = bread( ip->dev, bmap( ip, off/BSIZE ) );
m = min( n - tot, BSIZE - off%BSIZE );
memmove( bp->data + off%BSIZE, src, m );
log_write( bp );
brelse( bp );
}
if ( n > 0 && off > ip->size )
{
ip->size = off;
iupdate( ip );
}
return n;
}
How does all this result in the terminal displaying the characters? How does the terminal know to read fd 1 for display, and where to find fd 1? What is the format of fd 1? Is it a standard?
Below is the full path from printf to the terminal. The gist is that eventually, xv6 writes the character to the CPU's serial port.
QEMU is initialized with the flags -nographic or -serial mon:stdio which tell it to use the terminal to send data to, or receive data from the CPU's serial port.
Step 1) printf in forktest.c
void printf ( int fd, const char *s, ... )
{
write( fd, s, strlen( s ) );
}
void forktest ( void )
{
...
printf( 1, "fork test\n" );
...
}
Step 2) write in usys.S
.globl write
write:
movl $SYS_write, %eax
int $T_SYSCALL
ret
Step 3) sys_write in sysfile.c
int sys_write ( void )
{
...
argfd( 0, 0, &f )
...
return filewrite( f, p, n );
}
static int argfd ( int n, int *pfd, struct file **pf )
{
...
f = myproc()->ofile[ fd ]
...
}
Previously during system initialization, main in init.c was called where the stdin (0), stdout (1), and stderr (2) file descriptors are created. This is what argfd finds when looking up the file descriptor argument to sys_write.
int main ( void )
{
...
if ( open( "console", O_RDWR ) < 0 )
{
mknod( "console", 1, 1 ); // stdin
open( "console", O_RDWR );
}
dup( 0 ); // stdout
dup( 0 ); // stderr
...
}
The stdin|out|err are inodes of type T_DEV because they are created using mknod in sysfile.c
int sys_mknod ( void )
{
...
ip = create( path, T_DEV, major, minor )
...
}
The major device number of 1 that is used to create them is mapped to the console. See file.h
// Table mapping major device number to device functions
struct devsw
{
int ( *read )( struct inode*, char*, int );
int ( *write )( struct inode*, char*, int );
};
extern struct devsw devsw [];
#define CONSOLE 1
Step 4) filewrite in file.c
int filewrite ( struct file *f, char *addr, int n )
{
...
if ( f->type == FD_INODE )
{
...
writei( f->ip, addr + i, f->off, n1 )
...
}
...
}
Step 5) writei in fs.c
int writei ( struct inode *ip, char *src, uint off, uint n )
{
...
if ( ip->type == T_DEV )
{
...
return devsw[ ip->major ].write( ip, src, n );
}
...
}
The call to devsw[ ip->major ].write( ip, src, n )
becomes devsw[ CONSOLE ].write( ip, src, n ).
Previously during system initialization, consoleinit mapped this to the function consolewrite (see console.c)
void consoleinit ( void )
{
...
devsw[ CONSOLE ].write = consolewrite;
devsw[ CONSOLE ].read = consoleread;
...
}
Step 6) consolewrite in console.c
int consolewrite ( struct inode *ip, char *buf, int n )
{
...
for ( i = 0; i < n; i += 1 )
{
consputc( buf[ i ] & 0xff );
}
...
}
Step 7) consoleputc in console.c
void consputc ( int c )
{
...
uartputc( c );
...
}
Step 8) uartputc in uart.c.
The out assembly instruction is used to write to the CPU's serial port.
#define COM1 0x3f8 // serial port
...
void uartputc ( int c )
{
...
outb( COM1 + 0, c );
}
Step 9) QEMU is configured to use the serial port for communication in the Makefile through the -nographic or -serial mon:stdio flags. QEMU uses the terminal to send data to the serial port, and to display data from the serial port.
qemu: fs.img xv6.img
$(QEMU) -serial mon:stdio $(QEMUOPTS)
qemu-nox: fs.img xv6.img
$(QEMU) -nographic $(QEMUOPTS)
fd==1 refers to stdout, or Standard Out. It's a common feature of Unix-like Operatin Systems. The kernel knows that it's not a real file. Writes to stdout are mapped to terminal output.
I have a C function that I want to convert to LUA but I'm getting strange results out of Lua:
unsigned short crc16(const char* pstrCurrent, int iCount)
{
unsigned short wCRC = 0;
int iIndex = 0;
while(--iCount >= 0)
{
wCRC = wCRC ^ ((int)(*pstrCurrent++) << 8);
printf ("WCRC = %u\n", wCRC);
}
return (wCRC & 0xFFFF);
}
and here is how I started the Lua:
local function crc16(keyCurrent, byteCount)
wCRC = 0
byteIndex = 1
local crcInput = {}
while byteCount > 0 do
print ("BYTE COUNT= " .. byteCount)
wCRC=bit32.bxor(wCRC, bit32.lshift(keyCurrent[byteIndex], 8))
print ( "WCRC = " .. wCRC )
byteCount = byteCount-1
byteIndex = byteIndex+1
end
end
Yes, I know the C function is incomplete, I just want to compare what's causing issues.
The prints of the WCRC is C and Lua print completely different numbers for the same input.
Is my Lua conversion incorrect? It is my second or third time using Lua so not quite sure what I'm doing wrong.
***************** UPDATE ********************
So here is the full C and LUA and a quick little test code:
unsigned short crc16(const char* pstrCurrent, int iCount)
{
unsigned short wCRC = 0;
int iIndex = 0;
// Perform the following for each character in the buffer
while(--iCount >= 0)
{
// Get the byte information for the calculation and
// advance the pointer
wCRC = wCRC ^ ((int)(*pstrCurrent++) << 8);
for(iIndex = 0; iIndex < 8; ++iIndex)
{
if(wCRC & 0x8000)
{
wCRC = (wCRC << 1) ^ 0x1021;
}
else
{
wCRC = wCRC << 1;
}
}
}
return (wCRC & 0xFFFF);
}
and the LUA conversion:
function crc16 (keyCurrent, iCount)
wCRC = 0
byteIndex = 1
iIndex = 0
local crcInput = {}
while iCount >= 1 do
wCRC = bit32.bxor (wCRC, bit32.lshift(keyCurrent[byteIndex], 8))
for iIndex=0,8 do
if (bit32.band (wCRC, 0x8000) ~= nil ) then
wCRC = bit32.bxor (bit32.lshift (wCRC, 1), 0x1021)
else
wCRC = bit32.lshift (wCRC, 1)
end
end
iCount = iCount-1
byteIndex = byteIndex+1
end
return (bit32.band (wCRC, 0xFFFF))
end
local dKey = {}
dKey = {8, 210, 59, 0, 18, 166, 254, 117}
print ( "CRC = " .. crc16 (dKey ,8) )
In C, for the same array I get: CRC16 = 567
In LUA, I get: CRC = 61471
Can someone tell me what I'm doing wrong?
Thanks
It seems they yield the same results:
pure-C
WCRC = 18432
WCRC = 11520
WCRC = 16640
WCRC = 11520
pure-Lua
BYTE COUNT= 4
WCRC = 18432
BYTE COUNT= 3
WCRC = 11520
BYTE COUNT= 2
WCRC = 16640
BYTE COUNT= 1
WCRC = 11520
ASCII convertor:
What do you mean?
There's mistakes in altered Lua sample.
1. bit32.band() returns number. Number 0 not equals to 'nil', that's totally different type. You're trying to compare number with nil, and that check will fail always.2. for iIndex=0,8 do iterates 9 times, including final index 8.
im having a problem with scanf and gets. and I kno that its bound to errors but I couldn't find any other way. This way, the name is printing out but It doesn't print out the first letter of it.
Here's my code:
#include <stdio.h>
float calculations(int age, float highBP, float lowBP);
char option;
int counter, age;
char temp_name[50];
float highBP, lowBP, riskF, optimalH = 120.0, optimalL = 80.0;
typedef struct {
char name[50]; /*which represents the patient’s name*/
int age; /*which represents the patient’s age*/
float highBP; /*highBP, which represents the patient’s high (systolic) blood pressure*/
float lowBP; /*lowBP, which represents the patient’s low (diastolic) blood pressure*/
float riskF; /*riskFactor, which represents the patient’s risk factor for stroke due to hypertension.*/
}patient;/*end structure patient*/
patient *pRecords[30];
void printMenu()
{
printf("\n---------------------------------------------------------\n");
printf("|\t(N)ew record\t(D)isplay db\t(U)pdate record\t|\n");
printf("|\t(L)oad disk\t(W)rite disk\t(E)mpty disk\t|\n");
printf("|\t(S)ort db\t(C)lear db\t(Q)uit \t\t|\n");
printf("---------------------------------------------------------\n");
printf("choose one:");
}/*end print menu*/
void enter()
{
if(counter == 30)
printf("database full.");
else{
printf("name: ");
while(getchar()=='\n');
gets(temp_name);
strcpy(pRecords[counter]->name , temp_name);
printf("age: "); scanf("%d", &age);
pRecords[counter]->age = age;
printf("highBP: "); scanf("%f", &highBP);
pRecords[counter]->highBP = highBP;
printf("lowBP: "); scanf("%f", &lowBP);
pRecords[counter]->lowBP = lowBP;
float temp = calculations(age, highBP,lowBP);
pRecords[counter]->riskF = temp;
/*printf("name: %s, age: %d, highbp:%.1f, lowBP:%.1f\n", pRecords[counter]->name,pRecords[counter]->age,pRecords[counter]->highBP,pRecords[counter]->lowBP);
printf("risk factor: %.1f\n", pRecords[counter]->riskF);*/
counter ++;
}
}/*end of void enter function*/
memallocate(int counter){
pRecords[counter] = (patient *)malloc (sizeof(patient));
}/*end memallocate function*/
void display()
{
printf("===============================\n");
int i;
for(i=0; i<counter; i++)
{
printf("name: %s\n", pRecords[i]->name);
printf("age: %d\n", pRecords[i]->age);
printf("bp: %.2f %.2f\n", pRecords[i]->highBP, pRecords[i]->lowBP);
printf("risk: %.2f\n\n", pRecords[i]->riskF);
}/*end of for loop*/
printf("========== %d records ==========", counter);
}/*end of display method*/
float calculations(int age, float highBP, float lowBP)
{ float risk;
if((highBP <= optimalH) && (lowBP <= optimalL))
{ risk = 0.0;
if(age >=50)
risk = 0.5;
}
else if(highBP <= optimalH && (lowBP>optimalL && lowBP <=(optimalL+10)))
{ risk= 1.0;
if(age >=50)
risk = 1.5;
}
else if ((highBP >optimalH && highBP <= (optimalH+10))&& lowBP <=optimalL)
{ risk= 1.0;
if(age >=50)
risk= 1.5;
}
else if((highBP > optimalH && highBP <=(optimalH+10)) && (lowBP >optimalL && lowBP <= (optimalL+10)))
{ risk= 2.0;
if(age >=50)
risk = 2.5;
}
else if(highBP < optimalH && (lowBP >(optimalL+11) && lowBP<(optimalL+20)))
{ risk = 3.0;
if(age >=50)
risk = 3.5;
}
else if((lowBP < optimalL) && (highBP >(optimalH+11) && highBP<(optimalH+20)))
{ risk = 3.0;
if(age >=50)
risk = 3.5;
}
else if((highBP>=(optimalH+11) && highBP <= (optimalH+20))&& (lowBP>=(optimalL+11) && lowBP<=(optimalL+20)))
{ risk = 4.0;
if(age >=50)
risk = 4.5;
}
else
{ risk = 5.0;
if(age >=50)
risk = 5.5;
}
return risk;
}/*end of calculation function*/
main()
{
printMenu();
char option=getchar();
while(option != 'q' || option != 'Q'){
if(option == 'N' || option == 'n')
{
memallocate(counter);
enter();
printMenu();
}
if (option == 'L' || option == 'l')
{
printMenu();
}
if(option == 'S' || option == 's')
{
printMenu();
}
if(option == 'D' || option == 'd')
{
display();
printMenu();
}
if(option == 'W' || option == 'w')
{
printMenu();
}
if(option == 'C' || option == 'c')
{
printMenu();
}
if(option == 'U' || option == 'u')
{
printMenu();
}
if(option == 'E' || option == 'e')
{
printMenu();
}
if(option == 'Q' || option == 'q')
{
exit(0);
}
option = getchar();
}/*end while*/
system("pause");
}/*end main*/
sample output:
---------------------------------------------------------
| (N)ew record (D)isplay db (U)pdate record |
| (L)oad disk (W)rite disk (E)mpty disk |
| (S)ort db (C)lear db (Q)uit |
---------------------------------------------------------
choose one: n
name: judy
age: 30
high bp: 110
low bp: 88
3
---------------------------------------------------------
| (N)ew record (D)isplay db (U)pdate record |
| (L)oad disk (W)rite disk (E)mpty disk |
| (S)ort db (C)lear db (Q)uit |
---------------------------------------------------------
choose one: n
name: cindy white
age: 52
high bp: 100.7
low bp: 89.4
---------------------------------------------------------
| (N)ew record (D)isplay db (U)pdate record |
| (L)oad disk (W)rite disk (E)mpty disk |
| (S)ort db (C)lear db (Q)uit |
---------------------------------------------------------
choose one: d
===============================
name: udy
age: 30
bp: 110.00 88.00
risk: 1.0
name: indy white
age: 52
bp: 100.70 89.40
risk: 1.5
========== 2 records ==========
Your while loop and use of gets() is generally not good practice.
Try something like:
fflush(stdin);
fgets(pRecords[counter]->name, sizeof(pRecords[counter]->name), stdin);
Try
if (strlen(pRecords[counter]->name) > 0)
{
pRecords[counter]->name[strlen(pRecords[counter]->name) - 1] = '\0';
}
You lose the first character to while(getchar()=='\n');. I don't know why that statement is necessary, but it loops until it gets a character that is not '\n' (which is 'j' and 'c' in your case).
while (getchar() == '\n');
This line eats the newlines plus one character. When getchar() does not return a newline, it has already consumed the first character.
Look at ungetc() to write that character back onto the stream.
This:
while(getchar()=='\n');
loops until it gets a non-newline, which will be the first character of the name.
Try this instead:
do
c = getchar();
while(c == '\n');
ungetc(c, stdin);