Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arduino reset #54

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 77 additions & 34 deletions app.py
Original file line number Diff line number Diff line change
@@ -1,73 +1,107 @@
import asyncio
import random
import sys
from typing import List
from typing import List, Optional

import code128

from const import SENDER_ADDRESS, TOKEN_ADDRESS, BAR_CODE_FILE_PATH, RECEIVER_LIST
from const import BAR_CODE_FILE_PATH, RECEIVER_LIST
from deployment import start_raiden_nodes
from raiden import RaidenNode, RaidenNodeMock
from track_control import TrackControl, ArduinoSerial, MockSerial
from track_control import (
TrackControl,
ArduinoSerial,
ArduinoTrackControl,
MockArduinoTrackControl,
BarrierEventTaskFactory,
BarrierLoopTaskRunner,
KeepAliveTaskRunner
)
from network import NetworkTopology
import logging

from utils import wait_for_event

log = logging.getLogger()

ADDRESS_MAP = {address: index for index, address in enumerate(RECEIVER_LIST)}


class BarcodeHandler():

_address_map = ADDRESS_MAP
_barcode_file_path = str(BAR_CODE_FILE_PATH)

def save_barcode(self, address, nonce):
address, nonce = self._process_args(address, nonce)
self._save_barcode(address, nonce)

def _process_args(self, address, nonce):
address = self._address_map[address]
return address, nonce

def _save_barcode(self, address, nonce):
barcode = code128.image("(" + str(address) + "," + str(nonce) + ")")
factor = 4
barcode = barcode.resize((int(barcode.width * factor), int(barcode.height * factor)))
barcode.save(self._barcode_file_path)
log.debug(f'Written current barcode to disk: {self._barcode_file_path}')


class TrainApp:

def __init__(self, track_control: TrackControl, raiden_nodes: List[RaidenNode],
network_topology: NetworkTopology):
network_topology: NetworkTopology,
barcode_handler: Optional[BarcodeHandler]=None):
self.track_control = track_control
self.raiden_nodes = raiden_nodes
self.network_topology = network_topology
self.barcode_handler = barcode_handler
self._track_loop = None
self._current_provider = None
self._provider_nonces = {provider.address: 0 for provider in self.raiden_nodes}

self._task_runners = [
KeepAliveTaskRunner(self.track_control),
BarrierLoopTaskRunner(self.track_control)
]
self._barrier_etf = BarrierEventTaskFactory(self.track_control)

def start(self):
self.track_control.start()
self.track_control.connect()
for task in self._task_runners:
task.start()
self._track_loop = asyncio.create_task(self.run())

# FIXME make awaitable so that errors can raise
# FIXME handle gracefully
def stop(self):
try:
self._track_loop.cancel()
# TODO implement stop
# self.track_control.stop()
for task in self._task_runners:
task.stop()
except asyncio.CancelledError:
pass

async def run(self):
# TODO make sure that every neccessary task is running:
# (barrier_etf, barrier_ltr instantiated, etc)
log.debug("Track loop started")
self.track_control.power_on()
while True:
# Pick a random receiver
self._choose_and_set_next_provider()
self._set_next_provider()
provider = self._current_provider
current_nonce = self.current_nonce

# Generate barcode with current provider and nonce
self.create_new_barcode(
provider=RECEIVER_LIST.index(self.current_provider_address),
nonce=current_nonce
)

payment_received_task = asyncio.create_task(
provider.ensure_payment_received(
sender_address=self.network_topology.sender_address,
token_address=self.network_topology.token_address,
nonce=current_nonce, poll_interval=0.05)
nonce=self.current_nonce, poll_interval=0.05)
)
barrier_event_task = asyncio.create_task(
wait_for_event(self.track_control.barrier_event))
barrier_event_task = self._barrier_etf.create_await_event_task()
log.info('Waiting for payment to provider={}, nonce={}'.format(provider.address,
current_nonce))

# await both awaitables but return when one of them is finished first
done, pending = await asyncio.wait([payment_received_task, barrier_event_task],
return_when=asyncio.FIRST_COMPLETED)
Expand All @@ -76,41 +110,46 @@ async def run(self):
if payment_received_task.result() is True:
payment_successful = True
else:
assert barrier_event_task in done
assert payment_received_task in pending
# cancel the payment received task
for task in pending:
task.cancel()

if payment_successful is True:
log.info("Payment received")
self._increment_nonce_for_current_provider()
assert barrier_event_task in pending
await barrier_event_task
# increment the nonce after the barrier was triggered
self._increment_nonce_for_current_provider()
else:
log.info("Payment not received before next barrier trigger")
self.track_control.power_off()

payment_received_task = asyncio.create_task(
provider.ensure_payment_received(sender_address=SENDER_ADDRESS,
token_address=TOKEN_ADDRESS,
nonce=current_nonce,
provider.ensure_payment_received(sender_address=self.network_topology.sender_address,
token_address=self.network_topology.token_address,
nonce=self.current_nonce,
poll_interval=0.05)
)
await payment_received_task
if payment_received_task.result() is True:
self._increment_nonce_for_current_provider()
self.track_control.power_on()
log.info("Payment received, turning track power on again")
else:
# this shouldn't happen
# FIXME remove assert in production code
assert False

def _choose_and_set_next_provider(self):
self._current_provider = random.choice(self.raiden_nodes)
def _on_new_provider(self):
log.info(f'New provider chosen: {self.current_provider_address}')
if self.barcode_handler is not None:
self.barcode_handler.save_barcode(self.current_provider_address, self.current_nonce)

def create_new_barcode(self, provider, nonce):
barcode = code128.image("(" + str(provider) + "," + str(nonce) + ")")
factor = 4
barcode = barcode.resize((int(barcode.width * factor), int(barcode.height * factor)))
barcode.save(str(BAR_CODE_FILE_PATH))
def _set_next_provider(self):
self._current_provider = random.choice(self.raiden_nodes)
self._on_new_provider()

@property
def current_provider_address(self):
Expand All @@ -129,9 +168,12 @@ def build_app(cls, network: NetworkTopology, mock_arduino=False, mock_raiden=Fal
raiden_node_cls = RaidenNode
if mock_arduino:
log.debug('Mocking Arduino serial')
serial_track_power = MockSerial()
arduino_track_control = MockArduinoTrackControl()
else:
serial_track_power = ArduinoSerial(port='/dev/ttyACM0', baudrate=9600, timeout=.1)
# arduino_serial = ArduinoSerial(port='/dev/ttyACM0', baudrate=9600, timeout=.1)
arduino_serial = ArduinoSerial(port='/dev/cu.usbmodem1421', baudrate=9600, timeout=.1)
arduino_track_control = ArduinoTrackControl(arduino_serial)

if mock_raiden:
raiden_node_cls = RaidenNodeMock
log.debug('Mocking RaidenNode')
Expand All @@ -147,6 +189,7 @@ def build_app(cls, network: NetworkTopology, mock_arduino=False, mock_raiden=Fal
'Not all raiden nodes could get started, check the log files for more info. Shutting down')
sys.exit()
raiden_nodes = list(raiden_nodes_dict.values())
track_control = TrackControl(serial_track_power)
track_control = TrackControl(arduino_track_control)

return cls(track_control, raiden_nodes, network)
barcode_handler = BarcodeHandler()
return cls(track_control, raiden_nodes, network, barcode_handler)
140 changes: 118 additions & 22 deletions arduino/track_control/track_control.ino
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include <avr/wdt.h>

// defines pins numbers
const int trigPin = 6;
const int echoPin = 7;
Expand All @@ -7,49 +9,143 @@ float distance;
bool measuring;
int value = 9;

// 0: Power is Off,
// 1: Power is On
int powerSensor = 0;

// 0: not measuring,
// 1: object is not close,
// 2: object is close
int barrierSensor = 0;

// 0: request Sensor Data/'ACK Signal'
// 1: power Track off
// 2: power Track on
// 3: turn barrier measuring off
// 4: turn barrier measuring on
// 5: Keepalive signal
// 6: request arduino reset
int inByte = 0;

int maxTriggerDistance = 20;

void setup() {
pinMode(3, OUTPUT); //make the pin (3) as output
pinMode(13, OUTPUT); //make the LED pin (13) as output
pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
pinMode(echoPin, INPUT); // Sets the echoPin as an Input
Serial.begin(9600); // Starts the serial communication
measuring = false;
establishContact();
//Aktiviere Watchdog mit 8s Zeitkonstante
wdt_enable(WDTO_8S);
}

void loop() {
int data;

if (Serial.available()> 0){
data = Serial.read();
}
if (Serial.available() > 0){
inByte = Serial.read();

if (data == 1) // Turn Train power on
{
digitalWrite(3, HIGH);
Serial.println(digitalRead(3));
}
if (data == 0) // Turn Train power off
{
digitalWrite(3, LOW);
Serial.println(digitalRead(3));
}

if (data == 2){ // Start measuiring
measuring = true;
}
switch (inByte) {
// send Sensor data
case 0:
updateSensorData();
delayMicroseconds(10);
sendAck();
sendSensorData();
break;
// Turn Train power off
case 1:
turnPowerOff();
sendAck();
break;
// Turn Train power on
case 2:
turnPowerOn();
sendAck();
break;
case 3:
measuring = false;
sendAck();
break;
case 4:
measuring = true;
sendAck();
break;
case 5:
// received keepalive, reset the watchdog timer
wdt_reset();
sendAck();
break;
default:
break;
}
}

if (measuring == true){
int value = getAverage();
if (value < 20){
Serial.println(value);
measuring = false;
if (value < maxTriggerDistance){
barrierSensor = 2;
}
else {
barrierSensor = 1;
}
}
else {
delayMicroseconds(10);
}
barrierSensor = 0;
delayMicroseconds(10);
}
}

void establishContact() {
while (Serial.available() <= 0) {
sendHandshake();
delay(300);
}

while (true) {
if (Serial.available() > 0){
inByte = Serial.read();
// we expect the ACK from the client
if (inByte == 0){
sendAck();
break;
}
}
}
}

void sendSensorData() {
Serial.write(powerSensor);
Serial.write(barrierSensor);
}

void sendAck() {
Serial.write('A');
}

void sendHandshake(){
Serial.write('H');
}

void turnPowerOff() {
digitalWrite(3, LOW);
}

void turnPowerOn() {
digitalWrite(3, HIGH);
}

void updateSensorData() {
if (digitalRead(3) == HIGH) {
powerSensor = 1;
} else {
powerSensor = 0;
}
}


int getDistance(){
// Clears the trigPin
digitalWrite(trigPin, LOW);
Expand Down
3 changes: 2 additions & 1 deletion raiden.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ async def query_for_payment_received(self, sender_address, token_address, nonce)
if event["event"] == "EventPaymentReceivedSuccess" and \
event["amount"] == 1 and \
event["identifier"] == nonce:
log.debug(f"{self} has payment-event for nonce: {nonce}")
return True
# Event not found in event list:
return False
Expand Down Expand Up @@ -133,6 +134,6 @@ async def query_for_started(self):

async def query_for_payment_received(self, sender_address, token_address, nonce):
# TODO remove
await asyncio.sleep(0.1)
await asyncio.sleep(3)
# always say the payment was received for now
return True
Loading