#include <ctype.h>
#include <errno.h>
#include <libgen.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/uio.h>

#include <linux/can.h>
#include <linux/can/raw.h>

int counters[2048] = { };
unsigned char datas[2048][8] = { };

typedef struct finding_s {
	char *source;
	void (*show)(unsigned char*);
} finding_t;

finding_t findings[2048] = { };

void show_1b12(unsigned char *data) {
	printf(" f=%.3lf Hz, I=%.3lf A", (data[0]+256*data[1])/1000., (data[4]+256*data[5])/1000.);
}

void show_foursigned16(unsigned char *data) {
	printf(" %d %d %d %d", (int16_t)(data[0]+256*data[1]), (int16_t)(data[2]+256*data[3]),(int16_t)(data[4]+256*data[5]),(int16_t)(data[6]+256*data[7]));
}

void show_twosigned32(unsigned char *data) {
	printf(" %d %d", (int32_t)(data[0]+256*data[1]+65536*data[2]+65536*256*data[3]), (int32_t)(data[4]+256*data[5]+65536*data[6]+65536*256*data[7]));
}

void show_ampsandstuff(unsigned char *data) {
	printf(" %.3lf %d", ((int32_t)(data[0]+256*data[1]+65536*data[2]+65536*256*data[3])*4)/1000., (int32_t)(data[4]+256*data[5]+65536*data[6]+65536*256*data[7]));
}

void show_ac_voltages(unsigned char *data) {
	printf(" V1=%.3lf V, V2=%.3lf V", (data[0]+256*data[1]+65536*data[2]+65536*256*data[3])/1000., (data[4]+256*data[5]+65536*data[6]+65536*256*data[7])/1000.);
}

void show_tempratures(unsigned char *data) {
	//printf(" T_t=%.3lf ℃, T_i=%.3lf ℃", (data[0]+256*data[1]+65536*data[2]+65536*256*data[3])/1000., (data[4]+256*data[5]+65536*data[6]+65536*256*data[7])/1000.);
	printf("\"transformer\":%.3lf,\"inverter\":%.3lf", (data[0]+256*data[1]+65536*data[2]+65536*256*data[3])/1000., (data[4]+256*data[5]+65536*data[6]+65536*256*data[7])/1000.);
}

void show_bms351(unsigned char *data) {
	printf(" RCV=%.1lf V, CC=%.1lf A, DC=%.1lf A, DCV=%.1lf V", (data[0]+256*data[1])/10., (data[2]+256*data[3])/10.,(data[4]+256*data[5])/10.,(data[6]+256*data[7])/10.);
}

void show_bms355(unsigned char *data) {
	printf(" SOC=%u %%, SOH=%u %%, HrResSOC=%.2lf %%%s", (data[0]+256*data[1]), (data[2]+256*data[3]),(data[4]+256*data[5])/100.,(data[6]+256*data[7])==0?"":" RESERVED BYTES NONZERO");
}

void show_bms356(unsigned char *data) {
	printf(" V=%.2lf V, I=%.1lf A, temp=%.1lf ℃%s", (data[0]+256*data[1])/100., ((int16_t)(data[2]+256*data[3]))/10., (data[4]+256*data[5])/10.,(data[6]+256*data[7])==0?"":" RESERVED BYTES NONZERO");
}

void show_bms359(unsigned char *data) {
	printf(" prot1=%02x, prot2=%02x, alarm1=%02x, alarm2=%02x, module=%u%s", data[0], data[1], data[2], data[3], data[4], data[5] != 'P'||data[6] != 'N'||data[7] != 0?" UNEXPECTED FIXED BYTES":"");
}

void show_bms370(unsigned char *data) {
	printf(" T_max=%u ℃, T_min=%u ℃, V_max=%.3lf V, V_min=%.3lf V", data[0], data[2], (data[4]+256*data[5])/1000., (data[6]+256*data[7])/1000., data[1]||data[3]?"":" RESERVED BYTES NONZERO");
}

void show_mstr305(unsigned char *data) {
	printf(" V=%.1lf V, I=%.1lf A, T=%.1lf, SOC=%.1lf%%", (data[0]+256*data[1])/10., ((int16_t)(data[2]+256*data[3]))/10., (data[4]+256*data[5])/10., (data[6]+256*data[7])/10.);
}

void show_bms371(unsigned char *data) {
	printf(" cell=%u, cell=%u, cell=%u, cell=%u", data[0], data[2], data[4], data[6], data[1]||data[3]||data[5]||data[7]?"":" RESERVED BYTES NONZERO");
}

void show_bms35e(unsigned char *data) {
	printf(" ");
	for (int i = 0; i < 8; i++) {
		if (data[i] <= 32 || data[i] >= 127) break;
		printf("%c", data[i]);
	}
}

void show_bms35c(unsigned char *data) {
	if (data[0]&0x80) printf(" CHARGE_ENABLE");
	if (data[0]&0x40) printf(" DISCHARGE_ENABLE");
	if (data[0]&0x20) printf(" RECCHGRI");
	if (data[0]&0x10) printf(" RECCHGRII");
	if (data[0]&0x08) printf(" RECCHGRFULL");
}

int main(int argc, char *argv) {
	struct timespec start, now;
	int msgs = 0;
	int s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
	struct sockaddr_can addr;
	char ctrlmsg[CMSG_SPACE(sizeof(struct timeval) + 3*sizeof(struct timespec) + sizeof(__u32))];
	struct msghdr msg;
	int nbytes;
	struct iovec iov;
	struct canfd_frame frame;

	if (s < 0) {
		perror("socket");
		return 1;
	}

	addr.can_family = AF_CAN;
	addr.can_ifindex = 0;

	if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
		perror("bind");
		exit(1);
	}


	/*
	findings[0x0001].show = show_foursigned16;
	findings[0x0005].show = show_foursigned16;
	findings[0x0010].show = show_foursigned16;

	findings[0x0032].source = "SLV2";
	findings[0x0031].show = show_foursigned16;
	findings[0x0031].source = "SLV1";
	findings[0x0032].show = show_foursigned16;
	findings[0x0032].source = "SLV2";
	findings[0x0041].show = show_foursigned16;
	findings[0x0041].source = "SLV1";
	findings[0x0042].show = show_foursigned16;
	findings[0x0042].source = "SLV2";
	findings[0x0181].show = show_ac_voltages;
	findings[0x0181].source = "SLV1";
	findings[0x0182].show = show_ac_voltages;
	findings[0x0182].source = "SLV2";
	*/
	findings[0x0191].show = show_tempratures;
	findings[0x0191].source = "SLV1";
	findings[0x0192].show = show_tempratures;
	findings[0x0192].source = "SLV2";
	/*
	findings[0x01a1].show = show_twosigned32;
	findings[0x01a1].source = "SLV1";
	findings[0x01a2].show = show_twosigned32;
	findings[0x01a2].source = "SLV2";
	findings[0x01b1].show = show_1b12;
	findings[0x01b1].source = "SLV1";
	findings[0x01b2].show = show_1b12;
	findings[0x01b2].source = "SLV2";
	findings[0x01c1].show = show_foursigned16;
	findings[0x01c1].source = "SLV1";
	findings[0x01c2].show = show_foursigned16;
	findings[0x01c2].source = "SLV2";
	findings[0x01d1].show = show_twosigned32;
	findings[0x01d1].source = "SLV1";
	findings[0x01d2].show = show_twosigned32;
	findings[0x01d2].source = "SLV2";
	findings[0x01e1].show = show_ampsandstuff;
	findings[0x01e1].source = "SLV1";
	findings[0x01e2].show = show_ampsandstuff;
	findings[0x01e2].source = "SLV2";
	findings[0x01f1].show = show_twosigned32;
	findings[0x01f1].source = "SLV1";
	findings[0x01f2].show = show_twosigned32;
	findings[0x01f2].source = "SLV2";
	findings[0x0201].show = show_twosigned32;
	findings[0x0201].source = "SLV1";
	findings[0x0202].show = show_twosigned32;
	findings[0x0202].source = "SLV2";
	findings[0x0305].source = "MSTR";
	findings[0x0305].show = show_mstr305;
	findings[0x0359].source = "BMS ";
	findings[0x0359].show = show_bms359;
	findings[0x0351].source = "BMS ";
	findings[0x0351].show = show_bms351;
	findings[0x0355].source = "BMS ";
	findings[0x0355].show = show_bms355;
	findings[0x0356].source = "BMS ";
	findings[0x0356].show = show_bms356;
	findings[0x035c].source = "BMS ";
	findings[0x035c].show = show_bms35c;
	findings[0x035e].source = "BMS ";
	findings[0x035e].show = show_bms35e;
	findings[0x0370].source = "BMS ";
	findings[0x0370].show = show_bms370;
	findings[0x0371].source = "BMS ";
	findings[0x0371].show = show_bms371;
	findings[0x0561].source = "MSTR";
	*/

	iov.iov_base = &frame;
        msg.msg_name = &addr;
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_control = &ctrlmsg;

	clock_gettime(CLOCK_REALTIME_COARSE, &start);

	while (1) {
		iov.iov_len = sizeof(frame);
		msg.msg_namelen = sizeof(addr);
		msg.msg_controllen = sizeof(ctrlmsg);
		msg.msg_flags = 0;

                nbytes = recvmsg(s, &msg, 0);
		//printf("%04x\n", frame.can_id);
		uint32_t id = frame.can_id&0x7FF;
		counters[id]++;
		memcpy(datas[id], frame.data, 8);
		msgs++;
		if (msgs > 1000) {
			clock_gettime(CLOCK_REALTIME_COARSE, &now);
			double elapsed = now.tv_sec - start.tv_sec + (now.tv_nsec - start.tv_nsec)/1000000000.;
			//printf("elapsed %lf\n", elapsed);
			printf("{");
			int start = 0;
			for (int i = 0x191; i < 0x193; i++) {
				if (start++ > 0) printf(",");
				printf("\"%s\":{", findings[i].source);
				if (counters[i]) {
					if (findings[i].show) findings[i].show(datas[i]);
				}
				printf("}");
			}
			printf("}\n");
			fflush(stdout);
			msgs = 0;
		}

		//if (frame.can_id == 0x00000561) {
		//	printf("%.*s", frame.len, frame.data);
			//fprint_long_canframe(stdout, &frame, NULL, view, max_dlen);
		//}
		//printf("\n");
	}


	exit(0);
}
