Commit 11a04edc authored by Miroslav Ondra's avatar Miroslav Ondra

Merge branch 'test'

parents 7db133a1 aa236db1
Pipeline #1210 passed with stages
in 2 minutes and 9 seconds
#variables:
# TEMPLATE_DISABLE: "1"
include:
project: docker/template
file: debian-pkg.yaml
##############################################################
build-stable:all:
extends: .build_for_stable
tags: ['amd64']
deploy-test:
extends: .deploy_test
deploy-release:
extends: .deploy_master
......@@ -2,14 +2,6 @@
#!! don't use # for inline comments
[MAIN]
webname = unipi
staticfiles = /opt/evok-mini/www/evok
;password = test
;rpcpassword = test
secret = ut5kB3hhf6VmZCujXGQ5ZHb1EAfiXHcy
port = 8088
enable_cors = False
cors_domains = *
modbus_port = 5020
modbus_address = 127.0.0.1
#modbus_address = *
......
......@@ -2,14 +2,6 @@
#!! don't use # for inline comments
[MAIN]
webname = unipi
staticfiles = /opt/evok-mini/www/evok
;password = test
;rpcpassword = test
secret = ut5kB3hhf6VmZCujXGQ5ZHb1EAfiXHcy
port = 8088
enable_cors = False
cors_domains = *
modbus_port = 5020
modbus_address = 127.0.0.1
#modbus_address = *
......
......@@ -8,7 +8,7 @@ Homepage: http://www.evok.cz
Package: evok-mini
Architecture: all
Depends: ${misc:Depends}, pigpio, python-pymodbus, python-pigpio, python-tornado, python-ow, python-toro, python-jsonrpclib
Depends: ${misc:Depends}, pigpio, python-pymodbus, python-pigpio, python-tornado, python-toro
Description: Web/websocket, modbus server for UniPi 1.x
Minimal version for limited servicing UniPi 1.x boards.
Designed for use with Mervis runtime for counting pulses on gpio via pigpio
......
......@@ -4,4 +4,3 @@ debian/systemd/evok-mini.service lib/systemd/system
debian/systemd/pigpiod.service lib/systemd/system
src/* opt/evok-mini
www opt/evok-mini
......@@ -4,7 +4,6 @@ import re
import unipig
from apigpio import I2cBus, GpioBus
from devices import *
import owclient
globals = {
'version': "1.0",
......@@ -84,86 +83,22 @@ def create_devices(Config):
circuit = res.group(2)
#print "%s %s %s" % (section,devclass, circuit)
try:
if devclass == 'OWBUS':
bus = Config.get(section, "owbus")
interval = Config.getfloat(section, "interval")
scan_interval = Config.getfloat(section, "scan_interval")
#### prepare 1wire process ##### (using thread affects timing!)
resultPipe = multiprocessing.Pipe()
taskPipe = multiprocessing.Pipe()
owbus = owclient.OwBusDriver(circuit, taskPipe, resultPipe, bus=bus,
interval=interval, scan_interval=scan_interval)
Devices.register_device(OWBUS, owbus)
elif devclass == 'SENSOR' or devclass == '1WDEVICE':
#permanent thermometer
bus = Config.get(section, "bus")
owbus = Devices.by_int(OWBUS, bus)
typ = Config.get(section, "type")
address = Config.get(section, "address")
interval = getintdef(Config, section, "interval", 15)
sensor = owclient.MySensorFabric(address, typ, owbus, interval=interval, circuit=circuit, is_static=True)
Devices.register_device(SENSOR, sensor)
elif devclass == '1WRELAY':
# Relays on DS2404
sensor = Config.get(section, "sensor")
sensor = Devices.by_int(SENSOR, sensor)
pin = Config.getint(section, "pin")
r = unipig.DS2408_relay(circuit, sensor, pin)
Devices.register_device(RELAY, r)
elif devclass == '1WINPUT':
# Inputs on DS2404
sensor = Config.get(section, "sensor")
sensor = Devices.by_int(SENSOR, sensor)
pin = Config.getint(section, "pin")
i = unipig.DS2408_input(circuit, sensor, pin)
Devices.register_device(INPUT, i)
elif devclass == 'I2CBUS':
if devclass == 'I2CBUS':
# I2C bus on /dev/i2c-1 via pigpio daemon
busid = Config.getint(section, "busid")
i2cbus = I2cBus(circuit=circuit, host='localhost', busid=busid)
Devices.register_device(I2CBUS, i2cbus)
elif devclass == 'MCP':
# MCP on I2c
i2cbus = Config.get(section, "i2cbus")
address = hexint(Config.get(section, "address"))
bus = Devices.by_int(I2CBUS, i2cbus)
mcp = unipig.UnipiMcp(bus, circuit, address=address)
Devices.register_device(MCP, mcp)
elif devclass == 'RELAY':
# Relays on MCP
mcp = Config.get(section, "mcp")
mcp = Devices.by_int(MCP, mcp)
pin = Config.getint(section, "pin")
r = unipig.Relay(circuit, mcp, pin)
Devices.register_device(RELAY, r)
elif devclass == 'GPIOBUS':
# access to GPIO via pigpio daemon
bus = GpioBus(circuit=circuit, host='localhost')
Devices.register_device(GPIOBUS, bus)
elif devclass == 'PCA9685':
#PCA9685 on I2C
i2cbus = Config.get(section, "i2cbus")
address = hexint(Config.get(section, "address"))
frequency = getintdef(Config, section, "frequency", 400)
bus = Devices.by_int(I2CBUS, i2cbus)
pca = unipig.UnipiPCA9685(bus, int(circuit), address=address, frequency=frequency)
Devices.register_device(PCA9685, pca)
elif devclass in ('AO', 'ANALOGOUTPUT'):
try:
#analog output on PCA9685
pca = Config.get(section, "pca")
channel = Config.getint(section, "channel")
#value = getfloatdef(Config, section, "value", 0)
driver = Devices.by_int(PCA9685, pca)
ao = unipig.AnalogOutputPCA(circuit, driver, channel)
except:
# analog output (PWM) on GPIO via pigpio daemon
gpiobus = Config.get(section, "gpiobus")
bus = Devices.by_int(GPIOBUS, gpiobus)
frequency = getintdef(Config, section, "frequency", 100)
value = getfloatdef(Config, section, "value", 0)
ao = unipig.AnalogOutputGPIO(bus, circuit, frequency=frequency, value=value)
# analog output (PWM) on GPIO via pigpio daemon
gpiobus = Config.get(section, "gpiobus")
bus = Devices.by_int(GPIOBUS, gpiobus)
frequency = getintdef(Config, section, "frequency", 100)
value = getfloatdef(Config, section, "value", 0)
ao = unipig.AnalogOutputGPIO(bus, circuit, frequency=frequency, value=value)
Devices.register_device(AO, ao)
elif devclass in ('INPUT', 'DI'):
# digital inputs on GPIO via pigpio daemon
......@@ -174,38 +109,6 @@ def create_devices(Config):
counter_mode = getstringdef(Config, section, "counter_mode", "disabled")
inp = unipig.Input(bus, circuit, pin, debounce=debounce, counter_mode=counter_mode)
Devices.register_device(INPUT, inp)
elif devclass in ('EPROM', 'EE'):
i2cbus = Config.get(section, "i2cbus")
address = hexint(Config.get(section, "address"))
size = getintdef(Config, section, "size", 256)
bus = Devices.by_int(I2CBUS, i2cbus)
ee = unipig.Eprom(bus, circuit, size=size, address=address)
Devices.register_device(EE, ee)
elif devclass in ('AICHIP',):
i2cbus = Config.get(section, "i2cbus")
address = hexint(Config.get(section, "address"))
bus = Devices.by_int(I2CBUS, i2cbus)
mcai = unipig.UnipiMCP342x(bus, circuit, address=address)
Devices.register_device(ADCHIP, mcai)
elif devclass in ('AI', 'ANALOGINPUT'):
chip = Config.get(section, "chip")
channel = Config.getint(section, "channel")
interval = getfloatdef(Config, section, "interval", 0)
bits = getintdef(Config, section, "bits", 14)
gain = getintdef(Config, section, "gain", 1)
correction = getfloatdef(Config, section, "correction", 5.564920867)
mcai = Devices.by_int(ADCHIP, chip)
try:
corr_rom = Config.get(section, "corr_rom")
eeprom = Devices.by_int(EE, corr_rom)
corr_addr = hexint(Config.get(section, "corr_addr"))
ai = unipig.AnalogInput(circuit, mcai, channel, bits=bits, gain=gain,
continuous=False, interval=interval, correction=correction, rom=eeprom,
corr_addr=corr_addr)
except:
ai = unipig.AnalogInput(circuit, mcai, channel, bits=bits, gain=gain,
continuous=False, interval=interval, correction=correction)
Devices.register_device(AI, ai)
except Exception, E:
print("Error in config section %s - %s" % (section, str(E)))
......
This diff is collapsed.
......@@ -75,7 +75,7 @@ class ModbusConnection(object):
def start_run(self, delegate):
self.stream.set_close_callback(lambda : self._on_close(delegate))
self.stream.read_bytes(1,callback=self._on_data)
self.stream.read_bytes(256,callback=self._on_data, partial=True)
def _on_close(self, delegate):
''' deregister itself from ModbusServer '''
......@@ -90,8 +90,8 @@ class ModbusConnection(object):
'''
if _logger.isEnabledFor(logging.DEBUG):
_logger.debug(" ".join([hex(ord(x)) for x in data]))
self.framer.processIncomingPacket(data, self.execute)
self.stream.read_bytes(1,callback=self._on_data)
self.framer.processIncomingPacket(data, self.execute, unit=0)
self.stream.read_bytes(256,callback=self._on_data, partial=True)
def send(self, message):
......
This diff is collapsed.
"""
Copyright 2009 Josh Marshall
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from base import private, start_server, config, coroutine
This diff is collapsed.
"""
============================
JSON-RPC Handler for Tornado
============================
This is a JSON-RPC server implementation, designed for use within the
Tornado framework. Usage is pretty simple:
>>> from tornadorpc.json import JSONRPCHandler
>>> from tornadorpc import start_server
>>>
>>> class handler(JSONRPCHandler):
>>> ... def add(self, x, y):
>>> ....... return x+y
>>>
>>> start_server(handler, port=8484)
It requires the jsonrpclib, which you can get from:
http://github.com/joshmarshall/jsonrpclib
Also, you will need one of the following JSON modules:
* cjson
* simplejson
From Python 2.6 on, simplejson is included in the standard
distribution as the "json" module.
"""
from tornadorpc_evok.base import BaseRPCParser, BaseRPCHandler
import jsonrpclib
from jsonrpclib.jsonrpc import isbatch, isnotification, Fault
from jsonrpclib.jsonrpc import dumps, loads
class JSONRPCParser(BaseRPCParser):
content_type = 'application/json-rpc'
def parse_request(self, request_body):
#try:
request = loads(request_body)
#except:
# Bad request formatting
# self.traceback()
# return self.faults.parse_error()
self._requests = request
self._batch = False
request_list = []
if isbatch(request):
self._batch = True
for req in request:
req_tuple = (req['method'], req.get('params', []))
request_list.append(req_tuple)
else:
self._requests = [request]
request_list.append(
(request['method'], request.get('params', []))
)
return tuple(request_list)
def parse_responses(self, responses):
if isinstance(responses, Fault):
return dumps(responses)
if len(responses) != len(self._requests):
return dumps(self.faults.internal_error())
response_list = []
for i in range(0, len(responses)):
request = self._requests[i]
response = responses[i]
if isnotification(request):
# Even in batches, notifications have no
# response entry
continue
rpcid = request['id']
version = jsonrpclib.config.version
if 'jsonrpc' not in request.keys():
version = 1.0
try:
response_json = dumps(
response, version=version,
rpcid=rpcid, methodresponse=True
)
except TypeError:
return dumps(
self.faults.server_error(),
rpcid=rpcid, version=version
)
response_list.append(response_json)
if not self._batch:
# Ensure it wasn't a batch to begin with, then
# return 1 or 0 responses depending on if it was
# a notification.
if len(response_list) < 1:
return ''
return response_list[0]
# Batch, return list
return '[ %s ]' % ', '.join(response_list)
class JSONRPCLibraryWrapper(object):
dumps = dumps
loads = loads
Fault = Fault
class JSONRPCHandler(BaseRPCHandler):
"""
Subclass this to add methods -- you can treat them
just like normal methods, this handles the JSON formatting.
"""
_RPC_ = JSONRPCParser(JSONRPCLibraryWrapper)
if __name__ == '__main__':
# Example Implementation
import sys
from tornadorpc.base import start_server
from tornadorpc.base import TestRPCHandler
class TestJSONRPC(TestRPCHandler):
_RPC_ = JSONRPCParser(JSONRPCLibraryWrapper)
port = 8181
if len(sys.argv) > 1:
port = int(sys.argv[1])
print 'Starting server on port %s' % port
start_server(TestJSONRPC, port=port)
"""
Various utilities for the TornadoRPC library.
"""
import inspect
def getcallargs(func, *positional, **named):
"""
Simple implementation of inspect.getcallargs function in
the Python 2.7 standard library.
Takes a function and the position and keyword arguments and
returns a dictionary with the appropriate named arguments.
Raises an exception if invalid arguments are passed.
"""
args, varargs, varkw, defaults = inspect.getargspec(func)
final_kwargs = {}
extra_args = []
has_self = inspect.ismethod(func) and func.im_self is not None
if has_self:
args.pop(0)
# (Since our RPC supports only positional OR named.)
if named:
for key, value in named.iteritems():
arg_key = None
try:
arg_key = args[args.index(key)]
except ValueError:
if not varkw:
raise TypeError("Keyword argument '%s' not valid" % key)
if key in final_kwargs.keys():
message = "Keyword argument '%s' used more than once" % key
raise TypeError(message)
final_kwargs[key] = value
else:
for i in range(len(positional)):
value = positional[i]
arg_key = None
try:
arg_key = args[i]
except IndexError:
if not varargs:
raise TypeError("Too many positional arguments")
if arg_key:
final_kwargs[arg_key] = value
else:
extra_args.append(value)
if defaults:
for kwarg, default in zip(args[-len(defaults):], defaults):
final_kwargs.setdefault(kwarg, default)
for arg in args:
if arg not in final_kwargs:
raise TypeError("Not all arguments supplied. (%s)", arg)
return final_kwargs, extra_args
"""
===========================
XML-RPC Handler for Tornado
===========================
This is a XML-RPC server implementation, designed for use within the
Tornado framework. Usage is pretty simple:
>>> from tornadorpc.xml import XMLRPCHandler
>>> from tornadorpc import start_server
>>>
>>> class handler(XMLRPCHandler):
>>> ... def add(self, x, y):
>>> ....... return x+y
>>>
>>> start_server(handler, port=8484)
It requires the xmlrpclib, which is built-in to Python distributions
from version 2.3 on.
"""
from tornadorpc.base import BaseRPCParser, BaseRPCHandler
import xmlrpclib
class XMLRPCSystem(object):
# Multicall functions and, eventually, introspection
def __init__(self, handler):
self._dispatch = handler._RPC_.dispatch
def multicall(self, calls):
for call in calls:
method_name = call['methodName']
params = call['params']
self._dispatch(method_name, params)
class XMLRPCParser(BaseRPCParser):
content_type = 'text/xml'
def parse_request(self, request_body):
#try:
params, method_name = xmlrpclib.loads(request_body)
#except:
# Bad request formatting, bad.
# return self.faults.parse_error()
return ((method_name, params),)
def parse_responses(self, responses):
try:
if isinstance(responses[0], xmlrpclib.Fault):
return xmlrpclib.dumps(responses[0])
except IndexError:
pass
try:
response_xml = xmlrpclib.dumps(responses, methodresponse=True)
except TypeError:
return self.faults.internal_error()
return response_xml
class XMLRPCHandler(BaseRPCHandler):
"""
Subclass this to add methods -- you can treat them
just like normal methods, this handles the XML formatting.
"""
_RPC_ = XMLRPCParser(xmlrpclib)
@property
def system(self):
return XMLRPCSystem(self)
if __name__ == '__main__':
# Test implementation
from tornadorpc.base import TestRPCHandler, start_server
import sys
port = 8282
if len(sys.argv) > 1:
port = int(sys.argv[1])
class TestXMLRPC(TestRPCHandler):
_RPC_ = XMLRPCParser(xmlrpclib)
@property
def system(self):
return XMLRPCSystem(self)
print 'Starting server on port %s' % port
start_server(TestXMLRPC, port=port)
This diff is collapsed.
<!DOCTYPE html>
<!--[if lt IE 7 ]>
<html class="ie ie6" lang="en"> <![endif]-->
<!--[if IE 7 ]>
<html class="ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]>
<html class="ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 9)|!(IE)]><!-->
<html lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>UniPi</title>
<meta name="description" content="">
<meta name="author" content="">
<!-- Favicons
================================================== -->
<link rel="shortcut icon" href="images/favicon.ico">
<!-- CSS
================================================== -->
<link rel="stylesheet" type="text/css" href="js/jquery/jquery.mobile-1.4.5.min.css"/>
<!-- Javascript
================================================== -->
<script rel="javascript" type="text/javascript" src="js/jquery/jquery-2.1.1.min.js"></script>
<script rel="javascript" type="text/javascript" src="js/jquery/jquery.mobile-1.4.5.min.js"></script>
<script rel="javascript" type="text/javascript" src="js/main.js"></script>
<script type="text/javascript">
$(document).ready(function() {
webSocketRegister();
updateValues();
});
</script>
<style>
.cmax8 {
max-width: 800px;
margin-left: auto;
margin-right: auto;
#position: static;
padding-left:0;
padding-right:0;
}
</style>
</head>
<body>
<!-- OVERVIEW page -->
<div data-role="page" id="page-overview">
<div data-role="header" class="cmax8" data-id="pageHeader" data-position="fixed">
<h2>UniPi Control Panel</h2>
<img src="/js/jquery/images/spinner.svg" class="ui-btn-right" id="unipi_loading_spinner" style="max-height: 80%;visibility: hidden;"/>
</div>
<div data-role="main" class="ui-content cmax8">
<div data-role="tabs" >
<div data-role="navbar">
<ul>
<li><a href="#inputs" data-theme="a" data-ajax="false" class="ui-btn-active">Inputs</a></li>
<li><a href="#outputs" data-theme="a" data-ajax="false">Outputs</a></li>
<li><a href="#system" data-theme="a" data-ajax="false">System</a></li>
<li><a href="#configs" data-theme="a" data-ajax="false">Configuration</a></li>
</ul>
</div>
<div id="inputs" class="ui-content" data-inset="true">
<ul data-role="listview" id="inputs_list" data-count-theme="b" data-inset="true" data-split-icon="gear">
<li data-role="list-divider" data-theme="a" id="unipi_input_divider">Digital</li>
<li data-role="list-divider" data-theme="a" id="unipi_ai_divider">Analog</li>
<li data-role="list-divider" data-theme="a" id="unipi_temp_divider">1Wire Sensors/Devices</li>
</ul>
</div>
<!-- /inputs -->
<div id="outputs" class="ui-content" data-inset="true">
<ul data-role="listview" id="outputs_list" data-count-theme="b" data-inset="true" data-split-icon="gear">
<li data-role="list-divider" data-theme="a" id="unipi_digital_divider">Digital</li>
<li data-role="list-divider" data-theme="a" id="unipi_relay_divider">Relays</li>