esitarski / CrossMgr

Cyclo Cross Management Application
MIT License
40 stars 20 forks source link

Invelion Active Chip #107

Open cfuent-es opened 1 year ago

cfuent-es commented 1 year ago

Hello everybody,

I have an Invelion device, and I wrote the following Python script to fetch the passages:

import time
import socket
from datetime import datetime

def receive_data(sock):
    buffer = sock.recv(1024 * 4)
    buffer = buffer.hex()
    messages = []
    while len(buffer) > 0:
        index = buffer.find("a0")
        if index == -1:
            break
        message = buffer[index:index+42]
        messages.append(message)
        buffer = buffer[index+42:]
    return messages

def process_message(message):
    chip_code = message[34:38].upper()
    now = datetime.fromtimestamp(time.time())
    now_str = now.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
    file_name = chip_code + " " + now_str + ".txt"
    with open("passings/" + file_name, "a") as file:
        file.write(chip_code + " | " + now_str + "\n")
    file.close()

def client(host='192.168.0.178', port=4001):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_address = (host, port)
    print("Connecting to %s port %s" % server_address)
    sock.connect(server_address)
    try:
        readings, second, sends = [], datetime.now().second, 0
        while True:
            message = "A006FF8B010001CE"
            sock.sendall(bytes.fromhex(message))
            sends += 1
            messages = receive_data(sock)
            for msg in messages:
                size = int(msg[2:4], 16)
                if(size == 19):
                    readings.append(msg)
                    process_message(msg)
            old_second = second
            second = datetime.now().second
            if second != old_second:
                print("Second: %d" % second, "Sends: %d" % sends, "Readings: %d" % len(readings))
                readings = []
                sends = 0
            #time.sleep(1)

    except socket.error as e:
        print("Socket error: %s" % str(e))
    except Exception as e:
        print("Other exception: %s" % str(e))
    finally:
        print("Closing connection to the server")
        sock.close()

client()

I would like to integrate with CrossMgr, perhaps saving passes directly to the pass database. What would be the best format to deliver these tickets?

Thanks.

esitarski commented 1 year ago

Hi Cesar,

It looks like you need to write an adapter from your device to talk to CrossMgr. This is simple in principle, but gets more complex in practice (as is the case for any network communication).

There are two implementations of adapters in the CrossMgr code - CrossMgrImpinj and CrossMgrAlien. Respectively, these interface with LLRP-compliant readers (Impinj) and the old Alien procol readers. They log all the reads and buffer them if they cannot connect to CrossMgr. When there is a CrossMgr connection, they send the reads to it.

The tricky part of network programming is making it restart when something goes wrong. For example, if the network connection between CrossMgr and the Adapter goes down, or there was a power failure, or CrossMgr crashes and you have to restart it. You need everything to restore itself automatically, and have nothing hang, crash or lose tag reads.

This is all critical in a live race situation.

Briefly, the CrossMgrImpinj and CrossMgrAlien adapters support a "nested-loop" protocol. In the outer loop, they attempt to connect to CrossMgr. In the inner loop, they send reads. If they encounter errors in the inner loop, they fall-back to the outer loop which attempts to reconnect to CrossMgr. After the CrossMgr connection, there are a set of "handshake" exchanges where the Adapter waits for CrossMgr to ask for its identity, its local datetime, and finally, a command to start sending reads (including buffered reads if the connection was unavailable. The CrossMgr management code and the RFID reader code run in separate threads and communicate through queues. This enables recovery on errors on either end. The interface clearly shows the status of the connections as well as the detected antennas so that you know when you need to take action.

Adapters are remarkably difficult to write and test (I fixed some bugs in CrossMgrImpinj 6 months ago - it stopped working when it lost the CrossMgr network connection during the connection handshake).

My recommendation is to take a look at the CrossMgrImpinj or CrossMgrAlien code and adapt it to your specific device. In this code, you can see the tag format, handshake protocol and reconnection logic. You could also choose to do a minimal implementation without error checking or recovery (much easier, but unreliable). This could be a lot of work.

Another option is to buy a used Impinj R1000 from ebay (often available for <$200 USD) and run CrossMgrImpinj. 100s of organizers have done this successfully in 1000s of races.

On Tue, Mar 7, 2023 at 6:23 PM Cesar Fuentes @.***> wrote:

Hello everybody,

I have an Invelion device, and I wrote the following Python script to fetch the passages:

import time import socket from datetime import datetime

def receive_data(sock): buffer = sock.recv(1024 * 4) buffer = buffer.hex() messages = [] while len(buffer) > 0: index = buffer.find("a0") if index == -1: break message = buffer[index:index+42] messages.append(message) buffer = buffer[index+42:] return messages

def process_message(message): chip_code = message[34:38].upper() now = datetime.fromtimestamp(time.time()) now_str = now.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] file_name = chip_code + " " + now_str + ".txt" with open("passings/" + file_name, "a") as file: file.write(chip_code + " | " + now_str + "\n") file.close()

def client(host='192.168.0.178', port=4001): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_address = (host, port) print("Connecting to %s port %s" % server_address) sock.connect(server_address) try: readings, second, sends = [], datetime.now().second, 0 while True: message = "A006FF8B010001CE" sock.sendall(bytes.fromhex(message)) sends += 1 messages = receive_data(sock) for msg in messages: size = int(msg[2:4], 16) if(size == 19): readings.append(msg) process_message(msg) old_second = second second = datetime.now().second if second != old_second: print("Second: %d" % second, "Sends: %d" % sends, "Readings: %d" % len(readings)) readings = [] sends = 0

time.sleep(1)

except socket.error as e:
    print("Socket error: %s" % str(e))
except Exception as e:
    print("Other exception: %s" % str(e))
finally:
    print("Closing connection to the server")
    sock.close()

client()

I would like to integrate with CrossMgr, perhaps saving passes directly to the pass database. What would be the best format to deliver these tickets?

Thanks.

— Reply to this email directly, view it on GitHub https://github.com/esitarski/CrossMgr/issues/107, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABGXKILNHPK7LNX3OXN6GDW267QDANCNFSM6AAAAAAVTDCEHQ . You are receiving this because you are subscribed to this thread.Message ID: @.***>

--

Edward Sitarski