Last active
June 12, 2019 05:13
-
-
Save lafka/511d162a4a716524df6ef8d28a5e9c50 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
, tinysix_g_addr#include "path.h" | |
#include <sqlite3.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <stdint.h> | |
#include <unistd.h> | |
// extern unsigned char send_associate(void); | |
extern unsigned char strResp[1000]; | |
extern int frm_len; | |
extern int Interrupt_Count; | |
extern unsigned char Modem_Response[1000]; | |
extern unsigned int send_req(unsigned char[],unsigned int); | |
extern unsigned char addr[8], root_mac_addr[8]; | |
extern unsigned int tinysix_g_acls_count; | |
extern uint8_t tinysix_g_acls[50][4]; | |
extern uint8_t tinysix_g_commission_panid[2]; | |
extern uint8_t tinysix_g_commission_channel; | |
extern uint8_t tinysix_g_commission_type; | |
extern uint8_t tinysix_g_commission_prefix[8]; | |
extern uint8_t tinysix_g_commission_llsec[16]; | |
int tinysix_g_addr_llocal = 0; | |
int hex_decode(uint8_t *out, size_t size, const char *in, uint8_t **ptr) { | |
int pos = size - 1; | |
int insize = strlen(in); | |
int first = 0 == insize % 2; | |
/** | |
* Loop in reverse to easily fix padding. | |
* | |
* The process is like this: | |
* - First lowercase input by OR'ing with ' ' (a single space) - bit 5 | |
* - check if it's a character or number by AND'ing with '@' - bit 6 | |
* - If `first` is initially 1 we're processing the right most nibble | |
* - If we encounter a separator character we flip `first` thus making | |
* the next nibble the first (remember we're using modulo to "guess" | |
* which nibble we're working on). | |
*/ | |
for (int i = strlen(in) - 1; i >= 0; i--) { | |
if (' ' == in[i] || '-' == in[i] || ':' == in[i]) { | |
/** | |
* if we've only processed the right most nibble we don't need to | |
* flip `first` since the next nibble will have same evenness | |
*/ | |
if (first != i % 2) { | |
pos--; | |
continue; | |
} | |
first = !first; | |
continue; | |
} | |
//if ( ! (('0' > in[pos] && in[pos] < '0') || ('a >= in[pos] && in[pos] <= 'f'))) { | |
// return -1; | |
//} | |
out[pos] |= ((in[i] | ' ') - ('@' & in[i] ? 0x57 : 0x30)) << (first == i % 2 ? 0 : 4); | |
if (first != i % 2) { | |
pos--; | |
} | |
} | |
// If the last processed character was first nibble processed `pos` has not | |
// been decremented yet | |
if (NULL != ptr) { | |
*ptr = &out[pos + (first ? 1 : 0)]; | |
} | |
return size - pos - (first ? 1 : 0); | |
} | |
int retry(unsigned int count, unsigned int delay, int (*fn)(void *), void *ptr) { | |
int status; | |
for (int i = 0; i < count; i++) { | |
printf("retry %d/%d for %p\n", i, count, fn); | |
status = fn(ptr); | |
printf(" -> returned %x\n", status); | |
if (1 == status) { | |
break; | |
} | |
usleep(delay * 1000); | |
} | |
return status; | |
} | |
/** | |
* Send an associate request for a specific panid | |
* | |
* Takes one uint16_t argument `panid`, sends ASSOCIATE | |
* and waits 500ms before checking the response. | |
* | |
* Returns 1 if OK, 0 if failed | |
*/ | |
int tinysix_associate(void *panid) { | |
uint8_t buf[] = {0x2b, 0x07, 0x02, 0x00, 0x02, 0x03, 0x00, 0x00}; | |
memcpy(&buf[6], panid, 2); | |
frm_len = 0; | |
Interrupt_Count = 0; | |
memset(Modem_Response, '\0', sizeof(Modem_Response)); | |
memset(strResp, '\0', sizeof(strResp)); | |
send_req(buf, sizeof(buf)); | |
return 5 <= frm_len && 0 == strResp[5]; | |
} | |
/** | |
* Send CONNSTATUS request, wait for 500 ms and check resposne | |
* | |
* Returns 1 if connected, 0 otherwise | |
*/ | |
int tinysix_connstatus(void *none) { | |
uint8_t buf[] = {0x2b, 0x05, 0x02, 0x00, 0x73, 0x63}; | |
frm_len = 0; | |
Interrupt_Count = 0; | |
memset(Modem_Response, '\0', sizeof(Modem_Response)); | |
memset(strResp, '\0', sizeof(strResp)); | |
send_req(buf, sizeof(buf)); | |
return 6 <= frm_len && 0 == strResp[5] && 0x01 == strResp[6]; | |
} | |
/** | |
* Send a decommissioning packet to `mac`. Optionally take a second | |
* parameter `tinysix_g_addr_llocal` in which case the packet will be transmitted | |
* using link-local prefix. | |
* | |
* Uses send req, the response will be put in strResp. | |
* If no reply returns 0 other wise 1 on success (reply received). | |
* | |
* Note that this does not validate that the decommissioning was succesfull | |
* it only checks if there was any bytes received after sending the decommision | |
* packet | |
*/ | |
int tinysix_decommission(void *none) { | |
uint8_t buf[] = { | |
0x2b, 0x2f, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x00, | |
0xff, 0xff, 0x01, 0x02, 0x02, 0x02, 0x03, 0xff, | |
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x4d, 0x53, 0x45, 0x44, 0x43, 0x4c}; | |
int size; | |
if (tinysix_g_addr_llocal) { | |
buf[2] = 0x45; | |
} | |
memcpy(&buf[3], tinysix_g_addr, 8); | |
printf("De-Commission Query[%d] ", (int) sizeof(buf)); | |
for (int i = 0; i < sizeof(buf); i++) { | |
printf("%02x ", buf[i]); | |
} | |
printf("\n"); | |
frm_len=0; | |
Interrupt_Count = 0; | |
memset(Modem_Response,'\0',sizeof(Modem_Response)); | |
memset(strResp,'\0',sizeof(strResp)); | |
size = send_req(buf, sizeof(buf)); | |
// @todo 2019-06-11; check that the decommissioning actually | |
// returned a status packet and that it was successfull | |
//strResp[size] = 0x00; | |
return size > 0; | |
} | |
/** | |
* Send a commissioning packet to `mac` | |
* | |
* - panid should be set in global `tinysix_g_commission_panid` | |
* - channel should be set in global `tinysix_g_commission_channel` | |
* - type should be set in global `tinysix_g_commission_type` | |
* - prefix should be set in global `tinysix_g_commission_prefix` | |
* - llsec should be set in global `tinysix_g_commission_llsec` | |
*/ | |
int tinysix_commission(void *none) { | |
uint8_t buf[] = { | |
0x2b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x00, | |
0xff, 0xff, 0x01, 0x02, 0x02, 0x02, 0x03, 0xff, | |
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x4d, 0x53, 0x45, 0x44, 0x43, 0x4c}; | |
int size; | |
if (tinysix_g_addr_llocal) { | |
buf[2] = 0x45; | |
} | |
memcpy(&buf[3], tinysix_g_addr, 8); | |
uint8_t *attr_panid = &buf[16]; | |
uint8_t *attr_channel = &buf[19]; | |
uint8_t *attr_type = &buf[21]; | |
uint8_t *attr_prefix = &buf[23]; | |
uint8_t *attr_llsec = &buf[32]; | |
memcpy(attr_panid, tinysix_g_commission_panid, sizeof(tinysix_g_commission_panid)); | |
*attr_channel = tinysix_g_commission_channel; | |
*attr_type = tinysix_g_commission_type; | |
memcpy(attr_prefix, tinysix_g_commission_prefix, sizeof(tinysix_g_commission_prefix)); | |
memcpy(attr_llsec, tinysix_g_commission_llsec, sizeof(tinysix_g_commission_llsec)); | |
if (tinysix_g_addr_llocal) { | |
buf[2] = 0x45; | |
} | |
memcpy(&buf[3], tinysix_g_addr, 8); | |
size = send_req(buf, sizeof(buf)); | |
return size > 0; | |
} | |
/** | |
* Add or remove ACLs from meter at address `mac` | |
* | |
* Argument `llocal` control if link-local is used | |
* Argument `op` MUST be either 0 for ADD or 1 for REM | |
* Argument `count` defines how many ACLs are in `acls` | |
* Argument `acls` a pointer to the buffer containing ACLs | |
*/ | |
static int _change_acls(uint8_t mac[8], int llocal, uint8_t op, int count, uint8_t (*acls)[4]) { | |
// byte number 16, 17 is Add/Remove (0/1) and ACL count | |
uint8_t buf[512] = { | |
0x2b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x1a, | |
0x01, 0x00}; | |
int size, len = 18; | |
if (llocal) { | |
buf[2] = 0x45; | |
} | |
memcpy(&buf[3], mac, 8); | |
buf[16] = op; | |
buf[17] = count; | |
memcpy(&buf[18], acls, 4 * count); | |
len += 4 * count; | |
buf[1] = len - 1; | |
printf("%s-ACL Query[%d] ", op == 1 ? "REM" : "ADD", len); | |
for (int i = 0; i < len; i++) { | |
printf("%02x ", buf[i]); | |
} | |
printf("\n"); | |
frm_len = 0; | |
Interrupt_Count = 0; | |
memset(Modem_Response, '\0', sizeof(Modem_Response)); | |
memset(strResp, '\0', sizeof(strResp)); | |
size = send_req(buf, len); | |
// @todo 2019-06-11; check that the decommissioning actually | |
// returned a status packet and that it was successfull | |
//strResp[size] = 0x00; | |
return size > 0; | |
} | |
/** | |
* Send REM-ACL command to device specified in the global `addr` | |
* | |
* Expects `tinysix_g_acls_count` and `tinysix_g_acls` is globally set | |
* the first being the number of ACLs the second being a buffer wiht space | |
* for atleast 4 * `tinysix_g_acls_count`. | |
* | |
* Additionally the global flag `tinysix_g_addr_llocal` | |
*/ | |
int tinysix_rem_acls(void *none) { | |
return _change_acls(addr, tinysix_g_addr_llocal, 0x01, tinysix_g_acls_count, tinysix_g_acls); | |
} | |
/** | |
* Send ADD-ACL command to device specified in the global `addr` | |
* | |
* Expects `tinysix_g_acls_count` and `tinysix_g_acls` is globally set | |
* the first being the number of ACLs the second being a buffer wiht space | |
* for atleast 4 * `tinysix_g_acls_count`. | |
* | |
* Additionally the global flag `tinysix_g_addr_llocal` | |
*/ | |
int tinysix_add_acls(void *none) { | |
return _change_acls(addr, tinysix_g_addr_llocal, 0x00, tinysix_g_acls_count, tinysix_g_acls); | |
} | |
/** | |
* Retrieve the commissionstatus and MAC for a meter | |
* | |
* Takes 4 arguments; | |
* - serialnum, the serialnumber of the device | |
* - makecode, an optional makecode or NULL if unspecified | |
* - commstatus, ptr to where value from database will be set. May be NULL | |
* - mac, optional pointer where value from database will be set. May be NULL | |
*/ | |
int get_meter_commissionstatus(char *serialnum, char *makecode, char *commstatus, uint8_t mac[8]) { | |
sqlite3 *db; | |
sqlite3_stmt *stmt; | |
int rows = 0, status; | |
char buf[32]; | |
const char *query = "SELECT macaddress, commissionstatus FROM rfmstr WHERE meterserialnumber = ?1 AND makecode = ?2"; | |
const char *query_nomake = "SELECT macaddress, commissionstatus FROM rfmstr WHERE meterserialnumber = ?1"; | |
sqlite3_open("/home/MRF.db", &db); | |
if (NULL != makecode) { | |
sqlite3_prepare_v2(db, query, -1, &stmt, 0); | |
} else { | |
sqlite3_prepare_v2(db, query_nomake, -1, &stmt, 0); | |
} | |
fprintf(stderr, "query: %s\n", sqlite3_expanded_sql(stmt)); | |
if (NULL != mac) { | |
strncpy(buf, sqlite3_column_text(stmt, 1), sizeof(buf)); | |
hex_decode(mac, 8, buf, NULL); | |
} | |
sqlite3_bind_text(stmt, 2, commstatus, -1, NULL); | |
while (SQLITE_ROW == (status = sqlite3_step(stmt))) { | |
strncpy(buf, sqlite3_column_text(stmt, 1), sizeof(buf)); | |
strncpy(buf, sqlite3_column_text(stmt, 2), sizeof(buf)); | |
rows++; | |
} | |
if (SQLITE_DONE != status) { | |
fprintf(stderr, "some error in database: %s\n", sqlite3_errmsg(db)); | |
} | |
sqlite3_finalize(stmt); | |
sqlite3_close(db); | |
if (rows > 1) { | |
return -1; | |
} else { | |
return 0 != rows; | |
} | |
} | |
/** | |
* Retrieve relevant information about a network for a specific serialnumber | |
* | |
* If a network was found the MAC and root will be set in `addr` and `raddr` | |
* respectively. The PAN of the network will be set in `panid`. | |
* | |
* If no network was found, or the meter does not have valid `networkid` set, | |
* this function will return 0 and none of the arguments will be changed. | |
* */ | |
int get_meter_networking(char *serialnum, uint8_t addr[8], uint8_t raddr[8], uint16_t *panid) { | |
sqlite3 *db; | |
sqlite3_stmt *stmt; | |
int status; | |
char buf[32]; | |
const char *query = "\ | |
SELECT meter.macaddress, root.macaddress, panid \n\ | |
FROM rfmstr meter, rfmstr root, network6 net \n\ | |
WHERE meter.meterserialnumber = '100002' \n\ | |
AND meter.networkid = net.networkid \n\ | |
AND root.meterserialnumber = net.rserialnumber \n\ | |
AND root.makecode = net.rmakecode"; | |
sqlite3_open("/home/MRF.db", &db); | |
sqlite3_prepare_v2(db, query, -1, &stmt, 0); | |
sqlite3_bind_text(stmt, 1, serialnum, -1, NULL); | |
printf("query: %s\n", sqlite3_expanded_sql(stmt)); | |
int rc = sqlite3_step(stmt); | |
if (SQLITE_DONE == rc) { | |
// nothing found | |
printf("No networking found for meter %s, device is probably uncommissioned", serialnum); | |
status = 0; | |
} else if (SQLITE_ROW == rc) { | |
// parse hex: meter mac address | |
strncpy(buf, sqlite3_column_text(stmt, 0), sizeof(buf)); | |
hex_decode(addr, 8, buf, NULL); | |
// parse hex: root mac address | |
strncpy(buf, sqlite3_column_text(stmt, 1), sizeof(buf)); | |
hex_decode(raddr, 8, buf, NULL); | |
// decode panid to big endian | |
strncpy(buf, sqlite3_column_text(stmt, 2), sizeof(buf)); | |
hex_decode((uint8_t *) panid, 2, buf, NULL); | |
status = 1; | |
} | |
sqlite3_finalize(stmt); | |
sqlite3_close(db); | |
return status; | |
} | |
int main() { | |
printf("hello %p\n", tinysix_associate); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment