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

Take args from JSON post data #8

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
54 changes: 46 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,47 @@
# Merch Embedded

# Merch Embedded

[![Join the chat at https://gitter.im/acm-uiuc/merch-development](https://badges.gitter.im/acm-uiuc/merch-development.svg)](https://gitter.im/acm-uiuc/merch-development?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

Possible information sources:

This repository contains all of the code related to controlling the underlying merch hardware (the vending machine).
Merch runs an embedded webserver that can be accessed at merch.acm.illinois.edu.

## API

Requests to the device must contain a valid token in the Authorization Header for a request to be processed.
As of now the only token will be given solely to the groot merch service, so if you wish to make merch requests go through groot.


### Vend a location

To vend a list of items, POST a request to `/vend`.
The request is of the form
```json
{
"transaction_id": 1,
"items": ["A1", "B2", "C3"]
}
```

The machine will respond with
```json
{
"transaction_id": 1,
"items": [
{"location": "A1", "error": null},
{"location": "B2", "error": "some sort of error"},
{"location": "C3", "error": null},

]
}
```

The errors that can take place while vending are currently:
* `"Invalid location"`


## Some related datasheets

[http://bajavending.com/Manual-de-Operacion-BevMax.pdf](http://bajavending.com/Manual-de-Operacion-BevMax.pdf)
* Has the right picture of the main controller board, no programming information though

Expand All @@ -18,15 +56,15 @@ has some useful info about what commands are sent

## License

This project is licensed under the University of Illinois/NCSA Open Source License. For a full copy of this license take a look at the LICENSE file.
This project is licensed under the University of Illinois/NCSA Open Source License. For a full copy of this license take a look at the LICENSE file.

When contributing new files to this project, preappend the following header to the file as a comment:
When contributing new files to this project, preappend the following header to the file as a comment:

```
Copyright © 2017, ACM@UIUC

This file is part of the Merch Project.
The Merch Project is open source software, released under the University of Illinois/NCSA Open Source License.
This file is part of the Merch Project.

The Merch Project is open source software, released under the University of Illinois/NCSA Open Source License.
You should have received a copy of this license in a file with the distribution.
```
48 changes: 39 additions & 9 deletions machine_controller/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,45 @@
merch = Merch()

@app.route('/vend', methods=['POST'])
def hello_world():
if request.headers.get('TOKEN', '') != token_value:
def vend():
if request.headers.get('Authorization', '') != token_value:
abort(401)
if 'item' not in request.args:
abort(400)
item = request.args['item']
merch.vend(item[0], int(item[1]))
return json.dumps({'success': True}), 200, {'ContentType': 'application/json'}
data = request.json
items = data['items']
transaction_id = data['transaction_id']

if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0')

statuses = []
merch.acquire()
for i, item in enumerate(items):
try:
merch.vend(item[0], int(item[1]))
statuses.append({'error': None, 'location': item})

except Exception as e:
# Some error occurred while vending
# I'd prefer to catch Merch.VendError's only, but if something else
# goes wrong, we still need to let the client know instead of
# throwing a 500
statuses.append({'error': str(e), 'location': item})
merch.release()

return jsonify(transaction_id=transaction_id, items=statuses)

@app.route('/status', methods=['GET'])
def status():
if request.headers.get('Authorization', '') != token_value:
abort(401)

notready = merch.inUse()

if(notready):
return ('', 503)
else:
# 200 to indicate success
return ('', 200)



if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', threaded=True)
38 changes: 29 additions & 9 deletions machine_controller/vend.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
# THE SOFTWARE.
import RPi.GPIO as GPIO
import time
from threading import Condition, Lock


class Merch:
Expand All @@ -42,7 +43,12 @@ class Merch:
ROW = [21, 20, 16]
COL = [19, 13]
MAX_LETTER = 'F'
MAX_NUMBER = '0'
MAX_NUMBER = 9

class VendError(Exception):
pass

InvalidLocationError = VendError("Invalid location")

def __init__(self, debug=False):
self.debug = debug
Expand All @@ -51,9 +57,22 @@ def __init__(self, debug=False):
self.__low()
self.__commit()

self.lock = Lock()

def __del__(self):
self.__cleanup()

def acquire(self):
self.lock.acquire()

def release(self):
self.lock.release()

def inUse(self):
# Trylock
return self.lock.locked()


def __cleanup(self):
''' Clean up all of the GPIO pins '''
GPIO.cleanup()
Expand Down Expand Up @@ -93,22 +112,20 @@ def vend(self, letter, number):
try:
char = ord(letter)
except TypeError:
raise TypeError('Letter %s does not represent a character' %
str(letter))
raise self.InvalidLocationError

# Maybe we should use the actual keypad value?
if char < ord('A') or char > ord('Z'):
raise ValueError('Invalid Letter: %s' % str(letter))
if char < ord('A') or char > ord(self.MAX_LETTER):
raise self.InvalidLocationError

num = 0
try:
num = int(number)
except TypeError:
raise TypeError('Number %s is not convertible to an integer' %
str(num))
raise self.InvalidLocationError

if num < 0 or num > 10:
raise ValueError('Number %d is not in the range 1-10' % num)
if num < 1 or num > self.MAX_NUMBER:
raise self.InvalidLocationError

self.__vend(letter, str(number))

Expand All @@ -123,6 +140,9 @@ def __vend(self, letter, number):
self.__sendKey(number)
self.__commit()

# Wait for vend to complete
time.sleep(10)

def __sendKey(self, key):
# TABLE OF OUTPUTS
# ROW = {ROW[0],ROW[1],ROW[2]}
Expand Down