├── charge └── TODO.md ├── shutdown └── TODO.md ├── watchdog └── TODO.md ├── meter ├── .gitignore ├── TODO.md ├── package.json └── meter.js ├── ocpp ├── doc │ ├── tpl │ │ ├── footer.html │ │ ├── header.html │ │ └── types.html │ ├── Makefile │ ├── gir-ocppjs-doc.js │ └── wsdl │ │ ├── ocpp_chargepointservice_1.2_final.wsdl │ │ └── ocpp_centralsystemservice_1.2_final.wsdl ├── lib │ ├── soap │ │ ├── index.js │ │ └── lib │ │ │ ├── http.js │ │ │ ├── soap.js │ │ │ ├── client.js │ │ │ └── server.js │ ├── main.js │ ├── start_server.js │ ├── start_client.js │ ├── utils.js │ ├── plugins.js │ └── simulators.js ├── client.js ├── server.js ├── gir-ocppjs.js ├── plugins │ ├── helloworld.js │ ├── cs-example.js │ ├── meter.js │ ├── rfid.js │ ├── cbxs-automatic-mode.js │ └── getting_started_with_plugins.html ├── package.json ├── test │ ├── test-cp-1.2.js │ ├── test-cp-1.5.js │ ├── test-cs-1.2.js │ ├── test-cs-1.5.js │ └── framework.js ├── NEWS └── README.md ├── display └── TODO.md ├── rfid ├── MFRC522.pyc ├── README.md ├── Dump.py ├── Read.py ├── Write.py └── MFRC522.py ├── spi ├── build │ ├── lib.linux-armv7l-2.7 │ │ └── spi.so │ └── temp.linux-armv7l-2.7 │ │ └── spi.o ├── test_multi.py ├── setup.py ├── test-nRF.py ├── README.md ├── SPIGames │ └── SPIGames.ino ├── spi.c └── spi_multi.c ├── autostart └── TODO.md ├── node2socket └── n2s.js ├── README.md └── .gitignore /charge/TODO.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /shutdown/TODO.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /watchdog/TODO.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /meter/.gitignore: -------------------------------------------------------------------------------- 1 | metervalue -------------------------------------------------------------------------------- /ocpp/doc/tpl/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /ocpp/lib/soap/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/soap'); 2 | -------------------------------------------------------------------------------- /display/TODO.md: -------------------------------------------------------------------------------- 1 | output 2 | - ocpp status, 3 | - ip addresses, 4 | - meter value -------------------------------------------------------------------------------- /rfid/MFRC522.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwweidi/ChargeBerryPi/HEAD/rfid/MFRC522.pyc -------------------------------------------------------------------------------- /meter/TODO.md: -------------------------------------------------------------------------------- 1 | simulate a smart meter in software by polling status of the pin that controls the charging. -------------------------------------------------------------------------------- /spi/build/lib.linux-armv7l-2.7/spi.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwweidi/ChargeBerryPi/HEAD/spi/build/lib.linux-armv7l-2.7/spi.so -------------------------------------------------------------------------------- /spi/build/temp.linux-armv7l-2.7/spi.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwweidi/ChargeBerryPi/HEAD/spi/build/temp.linux-armv7l-2.7/spi.o -------------------------------------------------------------------------------- /meter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "meter", 3 | "version": "0.0.1", 4 | "dependencies": { 5 | "express": "^4.15.4" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /autostart/TODO.md: -------------------------------------------------------------------------------- 1 | - start the rfid reader 2 | - start the ocpp client 3 | - start the shutdown script 4 | - start the display output 5 | 6 | 7 | by autostart entries, and or crontab reboot 8 | 9 | additionally use forever 10 | 11 | -------------------------------------------------------------------------------- /ocpp/client.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // -*- coding: utf-8 -*- 3 | "use strict" 4 | 5 | /** 6 | * [ OCPP Simulator : Central System Simulator and Charge Point Simulator ] 7 | */ 8 | 9 | var starter = require('./lib/start_client.js'); 10 | 11 | 12 | /* main */ 13 | starter.main(); 14 | 15 | -------------------------------------------------------------------------------- /ocpp/server.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // -*- coding: utf-8 -*- 3 | "use strict" 4 | 5 | /** 6 | * [ OCPP Simulator : Central System Simulator and Charge Point Simulator ] 7 | */ 8 | 9 | var starter = require('./lib/start_server.js'); 10 | 11 | 12 | /* main */ 13 | starter.main(); 14 | 15 | -------------------------------------------------------------------------------- /ocpp/gir-ocppjs.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // -*- coding: utf-8 -*- 3 | "use strict" 4 | 5 | /** 6 | * [ OCPP Simulator : Central System Simulator and Charge Point Simulator ] 7 | */ 8 | 9 | var gir_ocpp_js = require('./lib/main.js'); 10 | 11 | 12 | /* main */ 13 | gir_ocpp_js.main(); 14 | 15 | -------------------------------------------------------------------------------- /spi/test_multi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import spi 4 | 5 | ret = spi.openSPI(speed=1000000) 6 | print "openSPI returns: ", ret 7 | fd = ret ["fd"] 8 | print "fd = ", fd 9 | 10 | print "Reading nRF24L01 status registers:" 11 | 12 | for x in range(28): 13 | dat = spi.transfer(fd, (x, 0)) 14 | print "nRF Register 0x%X: %X" % (x, dat[1]) 15 | 16 | spi.closeSPI (fd) 17 | -------------------------------------------------------------------------------- /node2socket/n2s.js: -------------------------------------------------------------------------------- 1 | // One-shot server. Note that the server cannot send a reply; 2 | // UNIX datagram sockets are unconnected and the client is not addressable. 3 | var unix = require('unix-dgram'); 4 | var fs = require('fs'); 5 | var SOCKNAME= '/tmp/python2ocpp'; 6 | 7 | try { fs.unlinkSync(SOCKNAME); } catch (e) { /* swallow */ } 8 | 9 | var server = unix.createSocket('unix_dgram', function(buf) { 10 | console.log('received ' + buf); 11 | }); 12 | server.bind(SOCKNAME); 13 | -------------------------------------------------------------------------------- /ocpp/plugins/helloworld.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Hello world plugins 3 | * 4 | */ 5 | 6 | 7 | var plugin = { 8 | 9 | name: 'Hello world!', 10 | description: 'Hello World plugin example', 11 | author: '', 12 | 13 | ocpp_version: '1.5', 14 | system: 'cp', 15 | 16 | onLoad: function() { 17 | this.log('Hi, I\'m the '+ plugin.name +' plugin !'); 18 | }, 19 | 20 | onUnload: function() { 21 | this.log('Goodbye !'); 22 | } 23 | 24 | }; 25 | 26 | module.exports = plugin; 27 | 28 | -------------------------------------------------------------------------------- /ocpp/doc/tpl/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Documentation 5 | 6 | 7 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /spi/setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup, Extension 2 | 3 | module1 = Extension('spi', sources = ['spi.c']) 4 | 5 | setup ( 6 | name = 'SPI-Py', 7 | author='Louis Thiery', 8 | url='https://github.com/lthiery/SPI-Py', 9 | download_url='https://github.com/lthiery/SPI-Py/archive/master.zip', 10 | version = '1.0', 11 | description = 'SPI-Py: Hardware SPI as a C Extension for Python', 12 | license='GPL-v2', 13 | platforms=['Linux'], 14 | ext_modules = [module1] 15 | ) 16 | -------------------------------------------------------------------------------- /spi/test-nRF.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | 4 | import spi 5 | 6 | # This is a very simple script that uses the rPi SPI port to read out the various registers in a nRF24L01+ device 7 | # connected to the rPI SPI port 0 8 | # It should be pretty self-explanitory 9 | 10 | 11 | status = spi.openSPI(speed=1000000) 12 | print "SPI configuration = ", status 13 | 14 | print "Reading nRF24L01 status registers:" 15 | 16 | for x in range(28): 17 | dat = spi.transfer((x, 0)) 18 | print "nRF Register 0x%X: %X" % (x, dat[1]) 19 | 20 | spi.closeSPI() 21 | -------------------------------------------------------------------------------- /ocpp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "girocppjs", 3 | "description": "Experimental OCPP Simulator", 4 | "version": "1.0.1", 5 | "author": "GIR", 6 | "homepage": "http://www.gir.fr/ocppjs", 7 | "license": "BSD", 8 | "engines": { 9 | "node": ">=0.6.10" 10 | }, 11 | "main": "gir-ocppjs.js", 12 | "dependencies": { 13 | "jayschema": "~0.2.0", 14 | "node-expat": "~2.3.16", 15 | "request": "~2.16.2", 16 | "rpio": "^0.9.17", 17 | "unix-dgram": "2.0.1", 18 | "websocket": "~1.0.8", 19 | "xml2js": "~0.2.2" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /spi/README.md: -------------------------------------------------------------------------------- 1 | SPI-Py: Hardware SPI as a C Extension for Python 2 | ====== 3 | 4 | COPYRIGHT (C) 2012 Louis Thiery. All rights reserved. Further work by Connor Wolf. 5 | 6 | This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License V2 as published by the Free Software Foundation. 7 | 8 | LIABILITY 9 | This program is distributed for educational purposes only and is no way suitable for any particular application, 10 | especially commercial. There is no implied suitability so use at your own risk! 11 | -------------------------------------------------------------------------------- /ocpp/test/test-cp-1.2.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // -*- coding: utf-8 -*- 3 | "use strict" 4 | 5 | var Test = require('./framework.js').Test; 6 | 7 | // initialize 8 | Test.init( { system: 'cp', version: '1.2' } ); 9 | 10 | // fill the array with the commands to test 11 | Test.commands = [ 12 | 'bootnotification', 13 | 'authorize', 14 | 'heartbeat', 15 | 'starttransaction', 16 | 'metervalues', 17 | 'stoptransaction', 18 | 'diagnosticsstatusnotification', 19 | 'statusnotification', 20 | 'firmwarestatusnotification' 21 | ]; 22 | 23 | // process tests 24 | Test.test(); 25 | 26 | -------------------------------------------------------------------------------- /ocpp/test/test-cp-1.5.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // -*- coding: utf-8 -*- 3 | "use strict" 4 | 5 | var Test = require('./framework.js').Test; 6 | 7 | // initialize 8 | Test.init( { system: 'cp', version: '1.5' } ); 9 | 10 | // fill the array with the commands to test 11 | Test.commands = [ 12 | 'bootnotification', 13 | 'authorize', 14 | 'heartbeat', 15 | 'starttransaction', 16 | 'metervalues', 17 | 'stoptransaction', 18 | 'diagnosticsstatusnotification', 19 | 'statusnotification', 20 | 'firmwarestatusnotification', 21 | 'datatransfer' 22 | ]; 23 | 24 | // process tests 25 | Test.test(); 26 | 27 | -------------------------------------------------------------------------------- /ocpp/test/test-cs-1.2.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // -*- coding: utf-8 -*- 3 | "use strict" 4 | 5 | var Test = require('./framework.js').Test; 6 | 7 | // initialize 8 | Test.init( { system: 'cs', version: '1.2' } ); 9 | 10 | // fill the array with the commands to test 11 | Test.commands = [ 12 | 'remote_reset', 13 | 'remote_changeconfiguration', 14 | 'remote_starttransaction', 15 | 'remote_stoptransaction', 16 | 'remote_unlockconnector', 17 | 'remote_getdiagnostics', 18 | 'remote_changeavailability', 19 | 'remote_updatefirmware', 20 | 'remote_clearcache', 21 | 'remote_starttransaction' 22 | ]; 23 | 24 | // process tests 25 | Test.test(); 26 | 27 | -------------------------------------------------------------------------------- /ocpp/doc/tpl/types.html: -------------------------------------------------------------------------------- 1 |

Basic data types :

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
SOAP type (XML schema)JSON type
s:stringJSON string
s:intJSON number
s:dateTimeJSON string, formatted as an ISO 8601 combined date and time, with a "Z" 19 | suffix (UTC timezone).
s:anyURIJSON string
s:booleanJSON boolean
30 | -------------------------------------------------------------------------------- /ocpp/plugins/cs-example.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Hello world plugins 3 | * 4 | */ 5 | 6 | 7 | var plugin = { 8 | 9 | name: 'Central System Plugin', 10 | description: 'Central System plugin example', 11 | author: '', 12 | 13 | ocpp_version: '1.5', 14 | system: 'cs', 15 | 16 | onLoad: function() { 17 | var self = this; 18 | 19 | self.onClientConnectionEvent(function(type, cbId) { 20 | switch(type) { 21 | case 'connected': 22 | self.log('[CS-Example] Hi '+ cbId +' !'); 23 | self.cs.call('GetLocalListVersion'); 24 | break; 25 | case 'disconnected': 26 | self.log('[CS-Example] Goodbye '+ cbId +' !'); 27 | break; 28 | } 29 | }); 30 | 31 | self.log('[CS-Example] Started.'); 32 | }, 33 | 34 | }; 35 | 36 | module.exports = plugin; 37 | 38 | -------------------------------------------------------------------------------- /spi/SPIGames/SPIGames.ino: -------------------------------------------------------------------------------- 1 | 2 | // inslude the SPI library: 3 | #include 4 | 5 | char buf[100]; 6 | volatile byte pos; 7 | volatile boolean process_it; 8 | 9 | void setup(){ 10 | Serial.begin(57600); 11 | //enable slave mode 12 | pinMode(MISO,OUTPUT); 13 | SPCR |= _BV(SPE); 14 | 15 | //initialize buffer 16 | pos = 0; 17 | 18 | SPI.attachInterrupt(); 19 | Serial.println("SPI Testing"); 20 | } 21 | 22 | ISR(SPI_STC_vect) 23 | { 24 | byte c = SPDR; 25 | if(pos ocpp_1.2.html; 8 | echo "

Experimental OCPP 1.2 over Websocket

" >> ocpp_1.2.html; 9 | cat tpl/types.html >> ocpp_1.2.html; 10 | echo "

Central System service

" >> ocpp_1.2.html; 11 | node ${PRG} wsdl/ocpp_centralsystemservice_1.2_final.wsdl >> ocpp_1.2.html; 12 | echo "

Charge Point service

" >> ocpp_1.2.html; 13 | node ${PRG} wsdl/ocpp_chargepointservice_1.2_final.wsdl >> ocpp_1.2.html; 14 | cat tpl/footer.html >> ocpp_1.2.html; 15 | 16 | ocpp_1.5: 17 | cat tpl/header.html > ocpp_1.5.html; 18 | echo "

Experimental OCPP 1.5 over Websocket

" >> ocpp_1.5.html; 19 | cat tpl/types.html >> ocpp_1.5.html; 20 | echo "

Central System service

" >> ocpp_1.5.html; 21 | node ${PRG} wsdl/ocpp_centralsystemservice_1.5_final.wsdl >> ocpp_1.5.html; 22 | echo "

Charge Point service

" >> ocpp_1.5.html; 23 | node ${PRG} wsdl/ocpp_chargepointservice_1.5_final.wsdl >> ocpp_1.5.html; 24 | cat tpl/footer.html >> ocpp_1.5.html; 25 | 26 | clean: 27 | rm ocpp_1.2.html; rm ocpp_1.5.html; 28 | -------------------------------------------------------------------------------- /ocpp/lib/main.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | /** 4 | * 5 | * 6 | * 7 | */ 8 | 9 | var UI = require('./ui.js'), 10 | OCPP = require('./ocpp-protocol.js'), 11 | Utils = require('./utils.js'); 12 | 13 | function main() { 14 | // if arguments in shell 15 | if(process.argv.length > 2) { 16 | var args = process.argv.slice(2); 17 | 18 | // non-documented option 19 | for(var i = 0; i < args.length; i++) { 20 | if(args[i] == '--with-attr-at') { 21 | OCPP.enableAttributesWithAt(); 22 | args.splice(i, 1); 23 | break; 24 | } 25 | } 26 | 27 | if (UI.parseCommand(args) == -1) { 28 | // if error just print the usage and quit 29 | UI.rl.close(); 30 | return; 31 | } 32 | } 33 | else { 34 | console.log("== GIR ocppjs - OCPP CentralSystem & ChargePoint Simulator =="); 35 | console.log(" > Quick start :"); 36 | UI.getHelp(); 37 | } 38 | 39 | // read WSDL files and fill the data structures 40 | OCPP.readWSDLFiles(); 41 | OCPP.buildJSONSchemas(); 42 | 43 | // launch the command line interface 44 | UI.commandLine(); 45 | } 46 | 47 | exports.main = main; 48 | 49 | -------------------------------------------------------------------------------- /ocpp/lib/start_server.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | /** 4 | * 5 | * 6 | * 7 | */ 8 | 9 | var UI = require('./ui.js'), 10 | OCPP = require('./ocpp-protocol.js'), 11 | Utils = require('./utils.js'); 12 | 13 | function main() { 14 | // if arguments in shell 15 | if(process.argv.length > 2) { 16 | var args = process.argv.slice(2); 17 | 18 | // non-documented option 19 | for(var i = 0; i < args.length; i++) { 20 | if(args[i] == '--with-attr-at') { 21 | OCPP.enableAttributesWithAt(); 22 | args.splice(i, 1); 23 | break; 24 | } 25 | } 26 | 27 | if (UI.parseCommand(args) == -1) { 28 | // if error just print the usage and quit 29 | UI.rl.close(); 30 | return; 31 | } 32 | } 33 | else { 34 | console.log("== GIR ocppjs - OCPP CentralSystem & ChargePoint Simulator =="); 35 | console.log(" > Quick start :"); 36 | UI.getHelp(); 37 | } 38 | 39 | // read WSDL files and fill the data structures 40 | OCPP.readWSDLFiles(); 41 | OCPP.buildJSONSchemas(); 42 | 43 | // launch the command line interface 44 | UI.parse('start_cs 9000'); 45 | } 46 | 47 | exports.main = main; 48 | 49 | -------------------------------------------------------------------------------- /ocpp/lib/start_client.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | /** 4 | * 5 | * 6 | * 7 | */ 8 | 9 | var UI = require('./ui.js'), 10 | OCPP = require('./ocpp-protocol.js'), 11 | Utils = require('./utils.js'); 12 | 13 | function main() { 14 | // if arguments in shell 15 | if(process.argv.length > 2) { 16 | var args = process.argv.slice(2); 17 | 18 | // non-documented option 19 | for(var i = 0; i < args.length; i++) { 20 | if(args[i] == '--with-attr-at') { 21 | OCPP.enableAttributesWithAt(); 22 | args.splice(i, 1); 23 | break; 24 | } 25 | } 26 | 27 | if (UI.parseCommand(args) == -1) { 28 | // if error just print the usage and quit 29 | UI.rl.close(); 30 | return; 31 | } 32 | } 33 | else { 34 | console.log("== GIR ocppjs - OCPP CentralSystem & ChargePoint Simulator =="); 35 | console.log(" > Quick start :"); 36 | UI.getHelp(); 37 | } 38 | 39 | // read WSDL files and fill the data structures 40 | OCPP.readWSDLFiles(); 41 | OCPP.buildJSONSchemas(); 42 | 43 | // launch the command line interface 44 | var serverIp = process.env.SERVER; 45 | UI.parse('start_cp ws://' + serverIp + ':9000/simulator cp1'); 46 | UI.parse('load rfid'); 47 | UI.parse('load meter'); 48 | } 49 | 50 | exports.main = main; 51 | 52 | -------------------------------------------------------------------------------- /rfid/Dump.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf8 -*- 3 | 4 | import RPi.GPIO as GPIO 5 | import MFRC522 6 | import signal 7 | 8 | continue_reading = True 9 | 10 | # Capture SIGINT for cleanup when the script is aborted 11 | def end_read(signal,frame): 12 | global continue_reading 13 | print "Ctrl+C captured, ending read." 14 | continue_reading = False 15 | GPIO.cleanup() 16 | 17 | # Hook the SIGINT 18 | signal.signal(signal.SIGINT, end_read) 19 | 20 | # Create an object of the class MFRC522 21 | MIFAREReader = MFRC522.MFRC522() 22 | 23 | # This loop keeps checking for chips. If one is near it will get the UID and authenticate 24 | while continue_reading: 25 | 26 | # Scan for cards 27 | (status,TagType) = MIFAREReader.MFRC522_Request(MIFAREReader.PICC_REQIDL) 28 | 29 | # If a card is found 30 | if status == MIFAREReader.MI_OK: 31 | print "Card detected" 32 | 33 | # Get the UID of the card 34 | (status,uid) = MIFAREReader.MFRC522_Anticoll() 35 | 36 | # If we have the UID, continue 37 | if status == MIFAREReader.MI_OK: 38 | 39 | # Print UID 40 | print "Card read UID: "+str(uid[0])+","+str(uid[1])+","+str(uid[2])+","+str(uid[3]) 41 | 42 | # This is the default key for authentication 43 | key = [0xFF,0xFF,0xFF,0xFF,0xFF,0xFF] 44 | 45 | # Select the scanned tag 46 | MIFAREReader.MFRC522_SelectTag(uid) 47 | 48 | # Dump the data 49 | MIFAREReader.MFRC522_DumpClassic1K(key, uid) 50 | 51 | # Stop 52 | MIFAREReader.MFRC522_StopCrypto1() 53 | -------------------------------------------------------------------------------- /ocpp/plugins/meter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Hello world plugins 3 | * 4 | */ 5 | var http = require('http'); 6 | 7 | var plugin = { 8 | 9 | name: 'kwh meter', 10 | description: 'read a kwh meter', 11 | author: '', 12 | 13 | ocpp_version: '1.5', 14 | system: 'cp', 15 | 16 | cfg: { 17 | interval: 60000, 18 | meterPort: 10000 19 | }, 20 | 21 | onLoad: function () { 22 | var self = this; 23 | 24 | plugin.cp = this.cp; 25 | 26 | this.log('Hi, I\'m the ' + plugin.name + ' plugin !'); 27 | 28 | var options = { 29 | host: 'localhost', 30 | port: plugin.cfg.meterPort, 31 | path: '/meter' 32 | }; 33 | 34 | setInterval(function () { 35 | 36 | http.get(options, function (res) { 37 | var body = ''; 38 | res.setEncoding('utf8'); 39 | res.on('data', function (chunk) { 40 | body = chunk; 41 | }); 42 | 43 | self.cp.call('MeterValues', 44 | { 45 | id: self.cp.id, 46 | values: [ 47 | { 48 | timestamp: new Date().toISOString(), 49 | value: body 50 | } 51 | ] 52 | } 53 | ); 54 | }) 55 | }, plugin.cfg.interval) 56 | 57 | }, 58 | 59 | onUnload: function () { 60 | this.log('Goodbye !'); 61 | } 62 | 63 | }; 64 | 65 | module.exports = plugin; 66 | 67 | -------------------------------------------------------------------------------- /ocpp/plugins/rfid.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Hello world plugins 3 | * 4 | */ 5 | 6 | // One-shot server. Note that the server cannot send a reply; 7 | // UNIX datagram sockets are unconnected and the client is not addressable. 8 | var unix = require('unix-dgram'); 9 | var fs = require('fs'); 10 | var rpio = require('rpio'); 11 | 12 | rpio.open(40, rpio.OUTPUT, rpio.LOW); 13 | var SOCKNAME = '/tmp/python2ocpp'; 14 | 15 | try { 16 | fs.unlinkSync(SOCKNAME); 17 | } catch (e) { /* swallow */ 18 | } 19 | 20 | var server = unix.createSocket('unix_dgram', function (buf) { 21 | console.log('received ' + buf); 22 | 23 | // call plugin below 24 | plugin.authorize('' + buf) 25 | }); 26 | server.bind(SOCKNAME); 27 | 28 | 29 | var plugin = { 30 | 31 | name: 'rfid', 32 | description: 'reads rfid from socket', 33 | author: '', 34 | 35 | ocpp_version: '1.5', 36 | system: 'cp', 37 | 38 | authorize: function (id) { 39 | this.cp.call('Authorize', {"idTag": id}); 40 | }, 41 | 42 | onLoad: function () { 43 | 44 | var self = this; 45 | plugin.cp = self.cp; 46 | 47 | this.log('Hi, I\'m the ' + plugin.name + ' plugin !'); 48 | 49 | self.onResult('Authorize', function (values) { 50 | self.log('Authorize response:' + JSON.stringify(values)); 51 | rpio.write(40, rpio.HIGH); 52 | setTimeout(function () { 53 | rpio.write(40, rpio.LOW); 54 | }, 5000); 55 | }); 56 | }, 57 | 58 | 59 | onUnload: function () { 60 | this.log('Goodbye rfid!'); 61 | } 62 | 63 | }; 64 | 65 | module.exports = plugin; 66 | 67 | -------------------------------------------------------------------------------- /meter/meter.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var fs = require('fs'); 3 | var app = express(); 4 | var fileName = './metervalue'; 5 | var currentKw = 0; 6 | 7 | app.get('/meter', function (req, res) { 8 | var data = fs.readFileSync(fileName); 9 | res.set('Content-Type', 'text/html'); 10 | res.send(data.toString()); 11 | }); 12 | 13 | 14 | app.post('/currentKw/:value', function (req, res) { 15 | 16 | var value = req.params.value; 17 | console.log('Received currentKw ' + value); 18 | 19 | var numVal = Number(value); 20 | 21 | if (isNaN(numVal)) { 22 | var status = 400; 23 | } else { 24 | status = 200; 25 | currentKw = numVal; 26 | } 27 | res.status(status).end() 28 | 29 | }); 30 | 31 | 32 | try { 33 | var data = fs.readFileSync(fileName); 34 | } catch (e) { 35 | data = 0; 36 | fs.writeFileSync(fileName, data.toString()); 37 | console.log('File ' + fileName + ' created'); 38 | } 39 | 40 | var frequency = 60; //per hour 41 | 42 | setInterval(function () { 43 | try { 44 | var data = fs.readFileSync(fileName); 45 | } catch (e) { 46 | data = 0; 47 | } 48 | 49 | data = Number(data); 50 | if (isNaN(data)) { 51 | console.error('Invalid content in ' + fileName + ': ' + data + ' is NaN'); 52 | } 53 | var delta = currentKw / frequency; 54 | data += delta; 55 | 56 | if (delta) { 57 | fs.writeFileSync(fileName, data); 58 | console.log('Meter value ' + data + ' written to '+ fileName); 59 | } 60 | 61 | }, frequency*1000); 62 | 63 | app.listen(10000, function () { 64 | console.log('Meter listening on port 10000!'); 65 | }); 66 | -------------------------------------------------------------------------------- /ocpp/lib/soap/lib/http.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Vinay Pulim 3 | * MIT Licensed 4 | */ 5 | 6 | var url = require('url'), 7 | req = require('request'); 8 | 9 | var VERSION = "0.2.0"; 10 | 11 | exports.request = function(rurl, data, callback, exheaders, exoptions) { 12 | var curl = url.parse(rurl); 13 | var secure = curl.protocol == 'https:'; 14 | var host = curl.hostname; 15 | var port = parseInt(curl.port || (secure ? 443 : 80)); 16 | var path = [curl.pathname || '/', curl.search || '', curl.hash || ''].join(''); 17 | var method = data ? "POST" : "GET"; 18 | var headers = { 19 | "User-Agent": "node-soap/" + VERSION, 20 | "Accept" : "text/html,application/xhtml+xml,application/xml", 21 | "Accept-Encoding": "none", 22 | "Accept-Charset": "utf-8", 23 | "Connection": "close", 24 | "Host" : host 25 | }; 26 | 27 | if (typeof data == 'string') { 28 | headers["Content-Length"] = Buffer.byteLength(data, 'utf8');; 29 | headers["Content-Type"] = "application/x-www-form-urlencoded"; 30 | } 31 | 32 | exheaders = exheaders || {}; 33 | for (var attr in exheaders) { headers[attr] = exheaders[attr]; } 34 | 35 | var options = { 36 | uri: curl, 37 | method: method, 38 | headers: headers 39 | }; 40 | 41 | exoptions = exoptions || {}; 42 | for (var attr in exoptions) { options[attr] = exoptions[attr]; } 43 | 44 | var request = req(options, function (error, res, body) { 45 | if (error) { 46 | callback(error); 47 | } else { 48 | callback(null, res, body); 49 | } 50 | }); 51 | request.on('error', callback); 52 | request.end(data); 53 | } 54 | -------------------------------------------------------------------------------- /rfid/Read.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf8 -*- 3 | 4 | import RPi.GPIO as GPIO 5 | import MFRC522 6 | import signal 7 | import socket 8 | import sys 9 | import time 10 | import os 11 | 12 | server_address = '/tmp/python2ocpp' 13 | # Make sure the socket does not already exist 14 | 15 | # Create a UDS socket 16 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) 17 | def connect_to_socket(): 18 | try: 19 | print >>sys.stdout, " connecting" 20 | sock.connect(server_address) 21 | except socket.error, msg: 22 | print >>sys.stderr, msg 23 | time.sleep(5) 24 | 25 | connect_to_socket() 26 | 27 | continue_reading = True 28 | 29 | # Capture SIGINT for cleanup when the script is aborted 30 | def end_read(signal,frame): 31 | global continue_reading 32 | print "Ctrl+C captured, ending read." 33 | continue_reading = False 34 | GPIO.cleanup() 35 | sock.close() 36 | 37 | # Hook the SIGINT 38 | signal.signal(signal.SIGINT, end_read) 39 | 40 | # Create an object of the class MFRC522 41 | MIFAREReader = MFRC522.MFRC522() 42 | 43 | # Welcome message 44 | print "Welcome to the MFRC522 data read example" 45 | print "Press Ctrl-C to stop." 46 | 47 | # This loop keeps checking for chips. If one is near it will get the UID and authenticate 48 | while continue_reading: 49 | 50 | # Scan for cards 51 | (status,TagType) = MIFAREReader.MFRC522_Request(MIFAREReader.PICC_REQIDL) 52 | 53 | # If a card is found 54 | if status == MIFAREReader.MI_OK: 55 | print "Card detected" 56 | 57 | # Get the UID of the card 58 | (status,uid) = MIFAREReader.MFRC522_Anticoll() 59 | 60 | # If we have the UID, continue 61 | if status == MIFAREReader.MI_OK: 62 | # send to ocpp 63 | message = str(uid[0])+str(uid[1])+str(uid[2])+str(uid[3]) 64 | try: 65 | sock.sendall(message) 66 | except socket.error, msg: 67 | print >>sys.stderr, msg 68 | connect_to_socket() 69 | 70 | # Print UID 71 | print "Card read UID: "+str(uid[0])+","+str(uid[1])+","+str(uid[2])+","+str(uid[3]) 72 | 73 | # This is the default key for authentication 74 | key = [0xFF,0xFF,0xFF,0xFF,0xFF,0xFF] 75 | 76 | # Select the scanned tag 77 | MIFAREReader.MFRC522_SelectTag(uid) 78 | 79 | # Authenticate 80 | status = MIFAREReader.MFRC522_Auth(MIFAREReader.PICC_AUTHENT1A, 8, key, uid) 81 | 82 | # Check if authenticated 83 | if status == MIFAREReader.MI_OK: 84 | MIFAREReader.MFRC522_Read(8) 85 | MIFAREReader.MFRC522_StopCrypto1() 86 | else: 87 | print "Authentication error" 88 | 89 | -------------------------------------------------------------------------------- /rfid/Write.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf8 -*- 3 | 4 | import RPi.GPIO as GPIO 5 | import MFRC522 6 | import signal 7 | 8 | continue_reading = True 9 | 10 | # Capture SIGINT for cleanup when the script is aborted 11 | def end_read(signal,frame): 12 | global continue_reading 13 | print "Ctrl+C captured, ending read." 14 | continue_reading = False 15 | GPIO.cleanup() 16 | 17 | # Hook the SIGINT 18 | signal.signal(signal.SIGINT, end_read) 19 | 20 | # Create an object of the class MFRC522 21 | MIFAREReader = MFRC522.MFRC522() 22 | 23 | # This loop keeps checking for chips. If one is near it will get the UID and authenticate 24 | while continue_reading: 25 | 26 | # Scan for cards 27 | (status,TagType) = MIFAREReader.MFRC522_Request(MIFAREReader.PICC_REQIDL) 28 | 29 | # If a card is found 30 | if status == MIFAREReader.MI_OK: 31 | print "Card detected" 32 | 33 | # Get the UID of the card 34 | (status,uid) = MIFAREReader.MFRC522_Anticoll() 35 | 36 | # If we have the UID, continue 37 | if status == MIFAREReader.MI_OK: 38 | 39 | # Print UID 40 | print "Card read UID: "+str(uid[0])+","+str(uid[1])+","+str(uid[2])+","+str(uid[3]) 41 | 42 | # This is the default key for authentication 43 | key = [0xFF,0xFF,0xFF,0xFF,0xFF,0xFF] 44 | 45 | # Select the scanned tag 46 | MIFAREReader.MFRC522_SelectTag(uid) 47 | 48 | # Authenticate 49 | status = MIFAREReader.MFRC522_Auth(MIFAREReader.PICC_AUTHENT1A, 8, key, uid) 50 | print "\n" 51 | 52 | # Check if authenticated 53 | if status == MIFAREReader.MI_OK: 54 | 55 | # Variable for the data to write 56 | data = [] 57 | 58 | # Fill the data with 0xFF 59 | for x in range(0,16): 60 | data.append(0xFF) 61 | 62 | print "Sector 8 looked like this:" 63 | # Read block 8 64 | MIFAREReader.MFRC522_Read(8) 65 | print "\n" 66 | 67 | print "Sector 8 will now be filled with 0xFF:" 68 | # Write the data 69 | MIFAREReader.MFRC522_Write(8, data) 70 | print "\n" 71 | 72 | print "It now looks like this:" 73 | # Check to see if it was written 74 | MIFAREReader.MFRC522_Read(8) 75 | print "\n" 76 | 77 | data = [] 78 | # Fill the data with 0x00 79 | for x in range(0,16): 80 | data.append(0x00) 81 | 82 | print "Now we fill it with 0x00:" 83 | MIFAREReader.MFRC522_Write(8, data) 84 | print "\n" 85 | 86 | print "It is now empty:" 87 | # Check to see if it was written 88 | MIFAREReader.MFRC522_Read(8) 89 | print "\n" 90 | 91 | # Stop 92 | MIFAREReader.MFRC522_StopCrypto1() 93 | 94 | # Make sure to stop reading for cards 95 | continue_reading = False 96 | else: 97 | print "Authentication error" 98 | -------------------------------------------------------------------------------- /ocpp/doc/gir-ocppjs-doc.js: -------------------------------------------------------------------------------- 1 | /* 2 | * XML-to-JSON program 3 | * The purpose of this program is to generate JSON examples from 4 | * a given XML-Schema 5 | */ 6 | 'use strict' 7 | 8 | var fs = require('fs'), 9 | xml2js = require('xml2js'), 10 | OCPP = require('../lib/ocpp-protocol.js'), 11 | Utils = require('../lib/utils.js'); 12 | 13 | 14 | var VERSION; 15 | var FILE; 16 | var TYPE; 17 | var methodTree; 18 | 19 | var parser = new xml2js.Parser(); 20 | 21 | var parseWSDL = function(err, data) { 22 | parser.parseString(data, function (err, result) { 23 | var messages = []; 24 | 25 | // if the `attribute @` option is enabled, replace the example 26 | if(OCPP.WITH_ATTR_AT) { 27 | OCPP.enableAttributesWithAt(); 28 | } 29 | 30 | // JSON Schemas 31 | OCPP.buildJSONSchemas(); 32 | var schemaTree = OCPP.JSONSchemas[VERSION][TYPE]; 33 | 34 | // fetch all the messages 35 | var msg = result['wsdl:definitions']['wsdl:message']; 36 | for (var m in msg) { 37 | var message = msg[m]['wsdl:part'][0]['$'].element; 38 | 39 | // only push request and response 40 | if (message.indexOf('Request') > 0 || message.indexOf('Response') > 0) 41 | messages.push(Utils.deleteNamespace(message)); 42 | } 43 | 44 | // for all the messages 45 | for (var i in messages) { 46 | 47 | var name = Utils.capitaliseFirstLetter(messages[i]); 48 | var args = OCPP.wsdl[VERSION][name]; 49 | 50 | // print 51 | if (messages[i].indexOf('Request') > 0) { 52 | console.log('

'+ 53 | name.replace('Request', '') 54 | +'

'); 55 | console.log('
  • Request example:'); 56 | console.log('
    ');
     57 |         console.log(JSON.stringify(args, null, 2));
     58 |         console.log('
  • '); 59 | } 60 | else { 61 | var methodName = name.replace('Response', ''); 62 | 63 | console.log('
  • Response example:'); 64 | console.log('
    ');
     65 |         console.log(JSON.stringify(args, null, 2));
     66 |         console.log('
  • '); 67 | 68 | console.log('
  • Request schema:'); 69 | console.log('
    ');
     70 |         console.log(JSON.stringify(schemaTree[methodName + 'Request'],
     71 | 				   null, 2));
     72 |         console.log('
  • '); 73 | 74 | console.log('
  • Response schema:'); 75 | console.log('
    ');
     76 |         console.log(JSON.stringify(schemaTree[methodName + 'Response'],
     77 | 				   null, 2));
     78 |         console.log('
'); 79 | } 80 | } 81 | 82 | }); 83 | }; 84 | 85 | function main() { 86 | var args = process.argv.slice(2); 87 | 88 | if (args.length < 1) { 89 | console.log('Usage: node gir-ocpp-doc.js \n\n') 90 | return; 91 | } 92 | 93 | /* non-documented option */ 94 | if(args[1] && args[1] == "--with-attr-at"){ 95 | OCPP.WITH_ATTR_AT = true; 96 | } 97 | 98 | VERSION = Utils.retrieveVersion(args[0]); 99 | FILE = args[0]; 100 | 101 | TYPE = "cp"; 102 | if(FILE.search("centralsystem") != -1) 103 | TYPE = "cs"; 104 | 105 | var vers = TYPE +"_"+ VERSION; 106 | 107 | OCPP.readFile(vers, FILE); 108 | methodTree = OCPP.methodTree[VERSION][TYPE]; 109 | 110 | fs.readFile(FILE, parseWSDL); 111 | } 112 | 113 | main(); 114 | 115 | -------------------------------------------------------------------------------- /ocpp/test/framework.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // -*- coding: utf-8 -*- 3 | "use strict" 4 | 5 | /** 6 | * Test framework for the girocppjs simulator. 7 | * 8 | */ 9 | 10 | var exec = require('child_process').exec; 11 | 12 | var Test = { 13 | 14 | centralSystem: null, 15 | chargePoint: null, 16 | 17 | cp: 'gir-ocppjs.js start_cp', 18 | cs: 'gir-ocppjs.js start_cs', 19 | 20 | options: null, 21 | args: null, 22 | 23 | currentCommandIndex: 0, 24 | testStarted: false, 25 | 26 | /** 27 | * 28 | */ 29 | init: function(opt) { 30 | if(process.argv.length < 3) { 31 | console.log('Usage: node [url] [id] [from]'); 32 | console.log(' .ex: node test-cp-1.2.js websocket'); 33 | console.log(' .ex: node test-cp-1.2.js soap http://192.168.1.12:9000/ '+ 34 | 'boxid http://192.168.1.25:9001'); 35 | process.exit(1); 36 | } 37 | 38 | var args = process.argv.slice(2), 39 | options = { 40 | system: opt.system || 'cp', 41 | version: opt.version || '1.5', 42 | transport: opt.transport || args[0] || 'websocket' 43 | }, 44 | args = args.slice(1); 45 | 46 | Test.options = options; 47 | Test.args = args; 48 | 49 | Test.startCentralSystem(); 50 | // delayed started 51 | setTimeout(function() { 52 | Test.startChargePoint(); 53 | 54 | if(options.system == 'cs') { 55 | setTimeout(function() { 56 | Test.chargePoint.stdin.write('bootnotification\n'); 57 | }, 1000); 58 | } 59 | }, 500); 60 | }, 61 | 62 | /** 63 | * 64 | */ 65 | startCentralSystem: function() { 66 | // only start a cs if no url specified 67 | if(Test.args.length != 0) 68 | return; 69 | 70 | var tp = '-t '+ Test.options.transport, 71 | prot = '-p ocpp'+ Test.options.version, 72 | cmd = 'node ../'+ Test.cs +' 9000 '+ prot +' '+ tp; 73 | 74 | Test.centralSystem = exec(cmd); 75 | Test.centralSystem.stdout.on('data', Test.stdOutHandler); 76 | }, 77 | 78 | /** 79 | * 80 | */ 81 | startChargePoint: function() { 82 | var tp = '-t '+ Test.options.transport, 83 | prot = '-p ocpp'+ Test.options.version, 84 | uriProt = Test.options.transport == 'websocket' ? 'ws' : 'http', 85 | uri = Test.args[0] || uriProt +'://localhost:9000', 86 | cpId = Test.args[1] || 'boxid', 87 | from = Test.args[2] || uriProt +'://localhost:9001', 88 | cmd = 'node ../'+ Test.cp +' '+ uri +' '+ cpId +' '+ prot +' '+ tp; 89 | 90 | Test.chargePoint = exec(cmd); 91 | Test.chargePoint.stdout.on('data', Test.stdOutHandler); 92 | }, 93 | 94 | /** 95 | * 96 | */ 97 | retrieveError: function(data) { 98 | if(data.toLowerCase().indexOf('error:') > -1) 99 | return true; 100 | 101 | var errors = ['NotImplemented', 'NotSupported', 'InternalError', 102 | 'ProtocolError', 'SecurityError', 'PropertyConstraintViolation', 103 | 'OccurenceConstraintViolation', 'TypeConstraintViolation', 104 | 'GenericError']; 105 | 106 | for(var e in errors) { 107 | if(data.indexOf(errors[e]) > -1) 108 | return true; 109 | } 110 | 111 | return false; 112 | }, 113 | 114 | /** 115 | * 116 | */ 117 | stdOutHandler: function(data) { 118 | if(Test.retrieveError(data)) { 119 | console.log('[ERROR] Test failed for `'+ 120 | Test.commands[Test.currentCommandIndex] +'\'.'); 121 | console.log('Output: '+ data); 122 | process.exit(1); 123 | } 124 | else { 125 | // TODO: find better way 126 | // only trigger the next action when the response of the current is 127 | // received 128 | if(Test.testStarted) { 129 | var next = false; 130 | 131 | if(Test.options.transport == 'websocket') { 132 | if(data.indexOf('<<') > 0 && data.indexOf('[3,') > 0) 133 | next = true; 134 | } 135 | else if(Test.options.transport == 'soap') { 136 | if(Test.options.system == 'cp' && data.indexOf('< 0) 137 | next = true; 138 | else if(Test.options.system == 'cs' && data.indexOf('< 0) 139 | next = true; 140 | } 141 | 142 | if(next) 143 | Test.testCommand(++Test.currentCommandIndex); 144 | } 145 | } 146 | }, 147 | 148 | /** 149 | * 150 | */ 151 | testCommand: function(index) { 152 | // if no command to test: end program 153 | if(Test.commands[index] == undefined) { 154 | process.exit(0); 155 | return; 156 | } 157 | 158 | var ptr = Test.commands[index].indexOf('remote_') > -1 159 | ? Test.centralSystem 160 | : Test.chargePoint; 161 | 162 | ptr.stdin.write(Test.commands[index] +'\n'); 163 | }, 164 | 165 | /** 166 | * 167 | */ 168 | test: function() { 169 | setTimeout(function() { 170 | Test.testStarted = true; 171 | Test.testCommand(Test.currentCommandIndex); 172 | }, 2000); 173 | } 174 | 175 | }; 176 | 177 | 178 | exports.Test = Test; 179 | -------------------------------------------------------------------------------- /ocpp/lib/soap/lib/soap.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Vinay Pulim 3 | * MIT Licensed 4 | */ 5 | 6 | var Client = require('./client').Client, 7 | Server = require('./server').Server, 8 | open_wsdl = require('./wsdl').open_wsdl, 9 | crypto = require('crypto'), 10 | WSDL = require('./wsdl').WSDL, 11 | https = require('https'), 12 | fs = require('fs'); 13 | 14 | var WSDL = require('./wsdl').WSDL; 15 | var _wsdlCache = {}; 16 | 17 | function _requestWSDL(url, options, callback) { 18 | if (typeof options === 'function') { 19 | callback = options; 20 | options = {}; 21 | } 22 | 23 | var wsdl = _wsdlCache[url]; 24 | if (wsdl) { 25 | callback(null, wsdl); 26 | } 27 | else { 28 | open_wsdl(url, options, function(err, wsdl) { 29 | if (err) 30 | return callback(err); 31 | else 32 | _wsdlCache[url] = wsdl; 33 | callback(null, wsdl); 34 | }) 35 | } 36 | } 37 | 38 | function createClient(url, options, callback, endpoint) { 39 | if (typeof options === 'function') { 40 | endpoint = callback; 41 | callback = options; 42 | options = {}; 43 | } 44 | endpoint = options.endpoint || endpoint; 45 | _requestWSDL(url, options, function(err, wsdl) { 46 | callback(err, wsdl && new Client(wsdl, endpoint)); 47 | }) 48 | } 49 | 50 | function listen(server, pathOrOptions, services, xml) { 51 | var options = {}, 52 | path = pathOrOptions; 53 | 54 | if (typeof pathOrOptions === 'object') { 55 | options = pathOrOptions; 56 | path = options.path; 57 | services = options.services; 58 | xml = options.xml; 59 | } 60 | 61 | var wsdl = new WSDL(xml || services, null, options); 62 | return new Server(server, path, services, wsdl); 63 | } 64 | 65 | function BasicAuthSecurity(username, password) { 66 | this._username = username; 67 | this._password = password; 68 | } 69 | 70 | BasicAuthSecurity.prototype.addHeaders = function (headers) { 71 | headers['Authorization'] = "Basic " + new Buffer((this._username + ':' + this._password) || '').toString('base64'); 72 | } 73 | 74 | BasicAuthSecurity.prototype.toXML = function() { 75 | return ""; 76 | } 77 | 78 | function ClientSSLSecurity(keyPath, certPath) { 79 | this.key = fs.readFileSync(keyPath); 80 | this.cert = fs.readFileSync(certPath); 81 | } 82 | 83 | ClientSSLSecurity.prototype.toXML = function (headers) { 84 | return ""; 85 | } 86 | 87 | ClientSSLSecurity.prototype.addOptions = function (options) { 88 | options.key = this.key; 89 | options.cert = this.cert; 90 | options.agent = new https.Agent(options); 91 | } 92 | 93 | function WSSecurity(username, password, passwordType) { 94 | this._username = username; 95 | this._password = password; 96 | this._passwordType = passwordType || 'PasswordText'; 97 | } 98 | 99 | var passwordDigest = function(nonce, created, password) { 100 | // digest = base64 ( sha1 ( nonce + created + password ) ) 101 | var pwHash = crypto.createHash('sha1'); 102 | var rawNonce = new Buffer(nonce || '', 'base64').toString('binary'); 103 | pwHash.update(rawNonce + created + password); 104 | var passwordDigest = pwHash.digest('base64'); 105 | return passwordDigest; 106 | } 107 | 108 | WSSecurity.prototype.toXML = function() { 109 | // avoid dependency on date formatting libraries 110 | function getDate(d) { 111 | function pad(n){return n<10 ? '0'+n : n} 112 | return d.getUTCFullYear()+'-' 113 | + pad(d.getUTCMonth()+1)+'-' 114 | + pad(d.getUTCDate())+'T' 115 | + pad(d.getUTCHours())+':' 116 | + pad(d.getUTCMinutes())+':' 117 | + pad(d.getUTCSeconds())+'Z'; 118 | } 119 | var now = new Date(); 120 | var created = getDate( now ); 121 | var expires = getDate( new Date(now.getTime() + (1000 * 600)) ); 122 | 123 | // nonce = base64 ( sha1 ( created + random ) ) 124 | var nHash = crypto.createHash('sha1'); 125 | nHash.update(created + Math.random()); 126 | var nonce = nHash.digest('base64'); 127 | 128 | return "" + 129 | "" + 130 | ""+created+"" + 131 | ""+expires+"" + 132 | "" + 133 | "" + 134 | ""+this._username+"" + 135 | (this._passwordType === 'PasswordText' ? 136 | ""+this._password+"" 137 | : 138 | ""+passwordDigest(nonce, created, this._password)+"" 139 | ) + 140 | ""+nonce+"" + 141 | ""+created+"" + 142 | "" + 143 | "" 144 | } 145 | 146 | exports.BasicAuthSecurity = BasicAuthSecurity; 147 | exports.WSSecurity = WSSecurity; 148 | exports.ClientSSLSecurity = ClientSSLSecurity; 149 | exports.createClient = createClient; 150 | exports.passwordDigest = passwordDigest; 151 | exports.listen = listen; 152 | exports.WSDL = WSDL; 153 | 154 | // XXX modif lib: log function 155 | exports.log = Server.log; 156 | 157 | -------------------------------------------------------------------------------- /ocpp/lib/soap/lib/client.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Vinay Pulim 3 | * MIT Licensed 4 | */ 5 | 6 | function findKey(obj, val) { 7 | for (var n in obj) if (obj[n] === val) return n; 8 | } 9 | 10 | var http = require('./http'), 11 | assert = require('assert'), 12 | url = require('url'); 13 | 14 | var Client = function(wsdl, endpoint) { 15 | this.wsdl = wsdl; 16 | this._initializeServices(endpoint); 17 | 18 | this.lastReponse = null; 19 | } 20 | 21 | Client.prototype.addSoapHeader = function(soapHeader, name, namespace, xmlns) { 22 | if(!this.soapHeaders){ 23 | this.soapHeaders = []; 24 | } 25 | if(typeof soapHeader == 'object'){ 26 | soapHeader = this.wsdl.objectToXML(soapHeader, name, namespace, xmlns, true); 27 | } 28 | 29 | this.soapHeaders.push(soapHeader); 30 | } 31 | 32 | Client.prototype.setEndpoint = function(endpoint) { 33 | this.endpoint = endpoint; 34 | this._initializeServices(endpoint); 35 | } 36 | 37 | Client.prototype.describe = function() { 38 | var types = this.wsdl.definitions.types; 39 | return this.wsdl.describeServices(); 40 | } 41 | 42 | Client.prototype.setSecurity = function(security) { 43 | this.security = security; 44 | } 45 | 46 | Client.prototype.setSOAPAction = function(SOAPAction) { 47 | this.SOAPAction = SOAPAction; 48 | } 49 | 50 | Client.prototype._initializeServices = function(endpoint) { 51 | var definitions = this.wsdl.definitions, 52 | services = definitions.services; 53 | for (var name in services) { 54 | this[name] = this._defineService(services[name], endpoint); 55 | } 56 | } 57 | 58 | Client.prototype._defineService = function(service, endpoint) { 59 | var ports = service.ports, 60 | def = {}; 61 | for (var name in ports) { 62 | def[name] = this._definePort(ports[name], endpoint ? endpoint : ports[name].location); 63 | } 64 | 65 | 66 | return def; 67 | } 68 | 69 | Client.prototype._definePort = function(port, endpoint) { 70 | var location = endpoint, 71 | binding = port.binding, 72 | methods = binding.methods, 73 | def = {}; 74 | for (var name in methods) { 75 | def[name] = this._defineMethod(methods[name], location); 76 | if (!this[name]) this[name] = def[name]; 77 | } 78 | return def; 79 | } 80 | 81 | Client.prototype._defineMethod = function(method, location) { 82 | var self = this; 83 | return function(args, callback) { 84 | if (typeof args === 'function') { 85 | callback = args; 86 | args = {}; 87 | } 88 | self._invoke(method, args, location, function(error, result, raw) { 89 | callback(error, result, raw); 90 | }) 91 | } 92 | } 93 | 94 | Client.prototype._invoke = function(method, arguments, location, callback) { 95 | var self = this, 96 | name = method.$name, 97 | input = method.input, 98 | output = method.output, 99 | style = method.style, 100 | defs = this.wsdl.definitions, 101 | ns = defs.$targetNamespace, 102 | encoding = '', 103 | message = '', 104 | xml = null, 105 | soapAction = this.SOAPAction ? this.SOAPAction(ns, name) : (method.soapAction || (((ns.lastIndexOf("/") != ns.length - 1) ? ns + "/" : ns) + name)), 106 | headers = { 107 | SOAPAction: '"' + soapAction + '"', 108 | //'Content-Type': "text/xml; charset=utf-8" 109 | 'Content-Type': "application/soap+xml; charset=utf-8" 110 | }, 111 | options = {}, 112 | alias = findKey(defs.xmlns, ns); 113 | 114 | // Allow the security object to add headers 115 | if (self.security && self.security.addHeaders) 116 | self.security.addHeaders(headers); 117 | if (self.security && self.security.addOptions) 118 | self.security.addOptions(options); 119 | 120 | if (input.parts) { 121 | assert.ok(!style || style == 'rpc', 'invalid message definition for document style binding'); 122 | message = self.wsdl.objectToRpcXML(name, arguments, alias, ns); 123 | (method.inputSoap === 'encoded') && (encoding = 'soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" '); 124 | } 125 | else if (typeof(arguments) === 'string') { 126 | message = arguments; 127 | } 128 | else { 129 | assert.ok(!style || style == 'document', 'invalid message definition for rpc style binding'); 130 | 131 | // XXX change namespace 132 | message = self.wsdl.objectToDocumentXML(input.$name, arguments, input.targetNSAlias, input.targetNamespace); 133 | } 134 | 135 | xml = "' + 139 | "" + 140 | (self.soapHeaders ? self.soapHeaders.join("\n") : "") + 141 | (self.security ? self.security.toXML() : "") + 142 | "" + 143 | "" + 144 | message + 145 | "" + 146 | ""; 147 | 148 | self.lastRequest = xml; 149 | 150 | http.request(location, xml, function(err, response, body) { 151 | if (err) { 152 | callback(err); 153 | } 154 | else { 155 | self.lastResponse = body; 156 | 157 | try { 158 | var obj = self.wsdl.xmlToObject(body); 159 | } 160 | catch (error) { 161 | return callback(error, response, body); 162 | } 163 | var result = obj.Body[output.$name]; 164 | 165 | // RPC/literal response body may contain element named after the method + 'Response' 166 | // This doesn't necessarily equal the ouput message name. See WSDL 1.1 Section 2.4.5 167 | /*if(!result) { 168 | result = obj.Body[name + 'Response']; 169 | }*/ 170 | callback(null, result, body); 171 | } 172 | }, headers, options); 173 | } 174 | 175 | exports.Client = Client; 176 | -------------------------------------------------------------------------------- /ocpp/NEWS: -------------------------------------------------------------------------------- 1 | 09 oct 2013; ocppjs 1.0.2 2 | 3 | * Added constraints in JSON Schema (enumerations, maxLength, etc) 4 | documentation. Simulator now enforces these constraints when verifying 5 | incoming messages. 6 | * Added JSON Schema control for MeterValues & StopTransaction message. 7 | * Fixed missing namespace for nested object in SOAP. 8 | * Fixed missing value in the "To" header for SOAP remote actions. 9 | 10 | 04 oct 2013; ocppjs 1.0.1 11 | 12 | * Added JSON Schema for requests and responses in the documentation files. 13 | * Changed the way XML attributes are mapped to JSON. In practice, this only 14 | changes the format for OCPP1.5 MeterValues & StopTransaction request 15 | messages. 16 | The central system simulator doesn't enforce this format yet, and will 17 | accept anything under the StopTransactionRequest.transactionData and 18 | MeterValuesRequest.values fields when receiving a message. 19 | * Added package.json file for easier installation. 20 | * Fixed invalid JSON example for OCPP1.2 MeterValuesRequest. 21 | * Fixed action name and namespaces issues with SOAP transport. 22 | 23 | 03 jun 2013; ocppjs 1.0.0 24 | 25 | * Fixed ocpp1.5 JSON payload structure for: 26 | . GetConfiguration request: "key" was a string instead of an array of 27 | strings 28 | . GetConfiguration response: "unknownKey" was a string instead of an array 29 | of strings 30 | . MeterValues & StopTransaction request: "value" was an int instead of 31 | an array of strings, "@value" was a object instead of an array of objects 32 | * Added websocket protocol name when accepting a new connection. 33 | * Added a plugin system allowing to define the behavior of a CS or CP 34 | simulator: 35 | . 'plugins', 'load' and 'unload' commands for listing, loading 36 | and unloading plugins. 37 | . Plugin API documentation and tutorial in 38 | plugins/getting_started_with_plugins.html. 39 | . 'cbxs' chargepoint plugin example with connectors management. 40 | * Minor fixes 41 | 42 | 02 may 2013; ocppjs 0.0.11 43 | 44 | * Added: framework for writing unit tests and 4 example files (in test/). 45 | * Payload errors are now supported in SOAP mode. 46 | * 'send_raw' and 'remote_send_raw' commands are now available in SOAP mode. 47 | . Added parameter -h for printing http headers. 48 | * In SOAP mode, header 'From' retrieves the local IP address instead of using 49 | 'localhost'. 50 | 51 | 22 apr 2013; ocppjs 0.0.10 52 | 53 | * Added SOAP transport support in Central system simulator. 54 | * Added SOAP remote actions support: 55 | . Added parameter -r in 'start_cp' command, defining the listening 56 | port for remote actions. If not defined, it defaults to the port specifed 57 | in the 'wsa5:From' URL. 58 | . Added parameter -f in 'start_cp' command, defining the "wsa5:From" 59 | URL in SOAP header. If not defined, it defaults to 60 | 'http://localhost:random_port'. 61 | . 'send' and 'send_raw' commands have been disabled for the SOAP mode. 62 | . This version does not detect SOAP payload errors yet. 63 | 64 | 09 apr 2013; ocppjs 0.0.9 65 | 66 | * SOAP client integration: 67 | . Requires third-party libraries: 'node-expat' and 'request'. 68 | . Added parameter -t for the 'start_cp' command in order to choose the 69 | simulator transport layer (websocket or SOAP). 70 | . Added 'set print_xml' command for displaying SOAP envelopes in XML format. 71 | . Remote actions and Central system simulator are not supported yet with 72 | soap transport. 73 | * New JSON attributes syntax: {"node":"value","@node":{"attr1": "value1"}}. 74 | 75 | 14 mar 2013; ocppjs 0.0.8 76 | 77 | * WebSocket ping enhancements : 78 | . Added client websocket ping. 79 | . Added parameter '-i' for the 'start_cp' command in order to set the 80 | WebSocket ping frequency. 81 | . Added 'websocket_ping_cs' and 'websocket_ping_cp' commands for manually 82 | sending one WebSocket ping from a central system simulator or a charge 83 | point simulator. 84 | . Added 'set' command for modifying the WebSocket ping frequency without 85 | interrupting the simulator (0 for disabling WebSocket pings). 86 | * 'tns:MeterValue' type is now supported. doc/gir-ocppjs-doc.js has been 87 | updated with the expected JSON structure for types with XML schema attributes 88 | attributes. The MeterValue type in ocpp 1.5 is currently the only one with 89 | such a structure. 90 | * Added 'InternalError' support when a procedure raises an exception. 91 | * Added payload error checking for CALLRESULT messages. 92 | * Prompt '>' now shows up right after entering a command. 93 | 94 | 07 mar 2013; ocppjs 0.0.7 95 | 96 | * Payload parsing now supports complex types and simple type restrictions. 97 | * Procedure call messages can be customized from command line interface using 98 | the new 'argument=value' syntax. 99 | * OCPP 1.2 & OCPP 1.5 commands are completely covered with the addition of 100 | firmware and diagnostics messages. 101 | * Enhancement of the command line interface reliability: bugfixes and 102 | deletion of multiple spaces. 103 | * Simulator is now tolerant to WSDL absence and can be launched from 104 | any directory. 105 | 106 | 27 feb 2013; ocppjs 0.0.6 107 | 108 | * Basic payload parsing (Property/Occurence/TypeConstraintViolation for 109 | simple types). Complex types and restrictions are not checked yet. 110 | * Commands schema is now loaded from wsdl files. This requires: 111 | . An additional third-party library: xml2js. 112 | . Valid ocpp wsdl files in doc/wdsl subdirectory. They are provided in the 113 | zip file. 114 | * A CentralSystem simulator can now support multiple comma-separated protocol 115 | versions on a single endpoint. 116 | * New commands: 117 | . 'send' & 'remote_send' to send a custom message in a valid envelope, to 118 | simulate invalid procedure names and payloads. 119 | . 'send_raw' & 'remote_send_raw' to send a custom string, to simulate 120 | invalid envelopes. 121 | 122 | 20 feb 2013; ocppjs 0.0.5 123 | 124 | * Conformance to DRAFT1 specification. 125 | * Updated command-line interface. start_cp & start_cs commands can now be 126 | passed directly as shell arguments. 127 | * Removed automatic bootnotification & heartbeat. All messages are 128 | triggered manually (type 'help' to get the list), except websocket ping. 129 | * Completed the command set to cover usual ocpp1.2 and ocpp1.5 commands (not 130 | available yet: firmware & diagnotics messages). 131 | 132 | 14 feb 2013; DRAFT1 133 | 134 | * First public draft specificaton. 135 | * Removed chargeBoxIdentity from message payload. 136 | 137 | 01 feb 2013; ocppjs 0.0.4 138 | 139 | * Added doc/gir-ocppjs-doc.js: autogenerate JSON examples from OCPP wsdl. 140 | 141 | 23 jan 2013; ocppjs 0.0.3 142 | 143 | * Added websocket ping interval in centralsystem simulator. 144 | * Added websocket logging for ping/pong frames. 145 | 146 | 11 jan 2013; ocppjs 0.0.2 147 | 148 | * Updated logs. 149 | * Bugfixes. 150 | 151 | 28 dec 2012; ocppjs 0.0.1 152 | 153 | * Initial version. 154 | -------------------------------------------------------------------------------- /ocpp/lib/utils.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | /** 4 | * 5 | * 6 | * 7 | */ 8 | 9 | 10 | /** Utils 11 | * Set of independant utilitary functions 12 | * 13 | * @api public 14 | */ 15 | var Utils = { 16 | 17 | /** 18 | * Log function 19 | * 20 | * @param {String} message to display 21 | * @param 22 | */ 23 | log: function(msg, type) { 24 | var d = new Date(), 25 | log = Utils.dateToString(d) + " "; 26 | 27 | if(type != undefined) { 28 | if(type == "cs") 29 | log += "cs: "; 30 | else if(type == "cp") 31 | log += "cp: "; 32 | else 33 | log += "cp#" + type +": "; 34 | } 35 | 36 | console.log(log + msg); 37 | }, 38 | 39 | /** 40 | * Convert a Date to String 41 | * Format: [YY-mm-dd hh:mm:ss] 42 | * 43 | * @param {Date} 44 | * @param {String} 45 | */ 46 | dateToString: function(date) { 47 | var year = Utils.addZeroPadding(date.getFullYear()), 48 | month = Utils.addZeroPadding(date.getMonth() + 1), 49 | day = Utils.addZeroPadding(date.getDate()), 50 | hours = Utils.addZeroPadding(date.getHours()), 51 | minutes = Utils.addZeroPadding(date.getMinutes()), 52 | seconds = Utils.addZeroPadding(date.getSeconds()); 53 | 54 | return "["+ year +"-"+ month +"-"+ day +" "+ hours +":"+ minutes +":"+ 55 | seconds +"]"; 56 | }, 57 | 58 | /** 59 | * Add zero-padding if needed 60 | * 61 | * @param {Number} Number of the day/month 62 | * @param {String} 63 | */ 64 | addZeroPadding: function(n) { 65 | return n < 10 ? '0' + n : '' + n; 66 | }, 67 | 68 | /** 69 | * Generate a random ID 70 | * 71 | * @return String 72 | */ 73 | makeId: function() { 74 | var text = "", 75 | possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 76 | 77 | for(var i = 0; i < 32; i++) 78 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 79 | 80 | return text; 81 | }, 82 | 83 | /** 84 | * Return the id of the element on an object if there's only one element. 85 | * Purpose: get the id of the unique CP for shortened UI commands 86 | * 87 | * @param {Object} Object 88 | * @return {Number} Number = id, -1 = error 89 | */ 90 | getId: function(obj) { 91 | var nb = 0, id = -1; 92 | 93 | for(var o in obj) { 94 | id = o; 95 | // more than 1 element = return error 96 | if (++nb > 1) 97 | return -1; 98 | } 99 | 100 | return id; 101 | }, 102 | 103 | /** 104 | * Retrieve OCPP version from string 105 | */ 106 | retrieveVersion: function(str) { 107 | // if array, last occurence 108 | if(str instanceof Array) { 109 | str = str[str.length - 1]; 110 | } 111 | 112 | var v = []; 113 | for(var i in str) { 114 | if(str[i] >= 0 && str[i] < 10) { 115 | v.push(str[i]); 116 | } 117 | } 118 | 119 | return v.join('.'); 120 | }, 121 | 122 | /* 123 | * Delete the namespace of a xml node 124 | */ 125 | deleteNamespace: function(str) { 126 | return str.split(':')[1]; 127 | }, 128 | 129 | isEmpty: function(obj) { 130 | for(var i in obj) { return false; } 131 | return true; 132 | }, 133 | 134 | capitaliseFirstLetter: function(string) { 135 | return string.charAt(0).toUpperCase() + string.slice(1); 136 | }, 137 | 138 | lowerFirstLetter: function(string) { 139 | return string.charAt(0).toLowerCase() + string.slice(1); 140 | }, 141 | 142 | getDateISOFormat: function() { 143 | return new Date().toISOString().split('.')[0] + 'Z'; 144 | }, 145 | 146 | validURL: function(str) { 147 | var re = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;-]*)/ig; 148 | 149 | return re.test(str); 150 | }, 151 | 152 | clone: function(obj) { 153 | if (null == obj || "object" != typeof obj) return obj; 154 | var copy = obj.constructor(); 155 | for (var attr in obj) { 156 | if (obj.hasOwnProperty(attr)) 157 | if(typeof obj[attr] == "object") 158 | copy[attr] = Utils.clone(obj[attr]); 159 | else 160 | copy[attr] = obj[attr]; 161 | } 162 | 163 | return copy; 164 | }, 165 | 166 | /* 167 | * Functions related to WSDL parsing 168 | */ 169 | 170 | JSON: { 171 | 172 | complexTypes: {}, 173 | simpleTypes: {}, 174 | 175 | /* 176 | * Get the object from a given type 177 | */ 178 | getPathFromType: function(types, type) { 179 | for (var c in types) { 180 | if(!Utils.isEmpty(types[c]['$']['name']) && !Utils.isEmpty(type)) { 181 | if (types[c]['$']['name'].toLowerCase() == type.toLowerCase()) { 182 | return types[c]; 183 | } 184 | } 185 | } 186 | 187 | return null; 188 | }, 189 | 190 | /* 191 | * Get elements of a type 192 | */ 193 | getSequenceOfType: function(types, type) { 194 | var seq = []; 195 | 196 | // get path 197 | var path = Utils.JSON.getPathFromType(types, type); 198 | 199 | // retrieve sequence 200 | var elements = null; 201 | if (path != null) { 202 | if (path['s:sequence'] != undefined) 203 | elements = path['s:sequence'][0]['s:element']; 204 | } 205 | 206 | // get all the elements of the sequence 207 | if (elements != null) { 208 | for (var e in elements) { 209 | seq.push(elements[e]['$']); 210 | 211 | // specific case for the 'value', add complex type 212 | if(elements[e]['$']['name'] == 'value') { 213 | seq[seq.length - 1]['s:complexType'] = elements[e]['s:complexType']; 214 | } 215 | } 216 | } 217 | 218 | return seq; 219 | } 220 | 221 | }, 222 | 223 | // Get network interface IPs, from: 224 | // http://stackoverflow.com/questions/3653065/get-local-ip-address-in-node-js 225 | getNetworkIPs: function() { 226 | var ignoreRE = /^(127\.0\.0\.1|::1|fe80(:1)?::1(%.*)?)$/i; 227 | 228 | var exec = require('child_process').exec; 229 | var cached; 230 | var command; 231 | var filterRE; 232 | 233 | switch (process.platform) { 234 | case 'win32': 235 | //case 'win64': // TODO: test 236 | command = 'ipconfig'; 237 | filterRE = /\bIPv[46][^:\r\n]+:\s*([^\s]+)/g; 238 | break; 239 | case 'darwin': 240 | command = 'ifconfig'; 241 | filterRE = /\binet\s+([^\s]+)/g; 242 | // filterRE = /\binet6\s+([^\s]+)/g; // IPv6 243 | break; 244 | default: 245 | command = 'ifconfig'; 246 | filterRE = /\binet\b[^:]+:\s*([^\s]+)/g; 247 | // filterRE = /\binet6[^:]+:\s*([^\s]+)/g; // IPv6 248 | break; 249 | } 250 | 251 | return function (callback, bypassCache) { 252 | if (cached && !bypassCache) { 253 | callback(null, cached); 254 | return; 255 | } 256 | // system call 257 | exec(command, function (error, stdout, sterr) { 258 | cached = []; 259 | var ip; 260 | var matches = stdout.match(filterRE) || []; 261 | //if (!error) { 262 | for (var i = 0; i < matches.length; i++) { 263 | ip = matches[i].replace(filterRE, '$1') 264 | if (!ignoreRE.test(ip)) { 265 | cached.push(ip); 266 | } 267 | } 268 | //} 269 | callback(error, cached); 270 | }); 271 | }; 272 | } 273 | 274 | }; 275 | 276 | 277 | module.exports = Utils; 278 | 279 | -------------------------------------------------------------------------------- /ocpp/lib/plugins.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | 4 | var Utils = require('./utils.js'), 5 | OCPP = require('./ocpp-protocol.js'); 6 | 7 | /** 8 | * 9 | * 10 | * 11 | */ 12 | 13 | var Plugins = { 14 | 15 | plugins: {}, 16 | 17 | PLUGINS_DIR: 'plugins/', 18 | 19 | // ref to Simulators 20 | Simulators: null, 21 | // ref to UI 22 | UI: null, 23 | 24 | /** 25 | * 26 | */ 27 | API: { 28 | log: Utils.log, 29 | 30 | parse: function(line) { 31 | return Plugins.UI.commandToObject(Plugins.UI.parseLineToTokens(line)); 32 | }, 33 | 34 | cp: { 35 | call: function(procName, args) { 36 | setTimeout(function() { 37 | Plugins.escapeStringArguments(args); 38 | Plugins.Simulators.commandCPtoCS(-1, procName, args); 39 | }, 500); 40 | } 41 | }, 42 | 43 | cs: { 44 | call: function(procName, args) { 45 | setTimeout(function() { 46 | Plugins.escapeStringArguments(args); 47 | Plugins.Simulators.commandCStoCP(-1, procName, args); 48 | }, 500); 49 | } 50 | }, 51 | 52 | ocpp_version: null, 53 | system: null, 54 | protocol: null, 55 | chargePointId: null 56 | 57 | }, 58 | 59 | /** 60 | * TODO: give pointers 61 | * 62 | */ 63 | init: function() {}, 64 | 65 | /** 66 | * TODO: give pointers 67 | * 68 | */ 69 | setAPIFields: function(prot, system, version, cbId) { 70 | Plugins.API.protocol = prot; 71 | Plugins.API.system = system; 72 | Plugins.API.ocpp_version = version; 73 | Plugins.API.chargePointId = cbId; 74 | }, 75 | 76 | /** 77 | * 78 | */ 79 | load: function(filename) { 80 | // if already exist, unload it 81 | if(Plugins.plugins[filename] != undefined) { 82 | Plugins.unload(filename); 83 | } 84 | 85 | var plugin = null; 86 | try { 87 | plugin = require(__dirname + '/../' + Plugins.PLUGINS_DIR + filename + 88 | '.js'); 89 | } 90 | catch(exception) { 91 | console.log('Error with the '+ filename +' plugin, invalid or '+ 92 | 'doesn\'t exist.'); 93 | return; 94 | } 95 | 96 | // check if plugin is compliant 97 | if(!Plugins.isPluginCompliant(filename, plugin)) { 98 | // errors are printed in the isPluginCompliant function 99 | return; 100 | } 101 | 102 | // 103 | Plugins.addHandlers(plugin); 104 | 105 | // add to array 106 | Plugins.plugins[filename] = plugin; 107 | 108 | // create a special scope 109 | var scope = Utils.clone(Plugins.API); 110 | scope.onResult = plugin.onResult; 111 | scope.onCall = plugin.onCall; 112 | scope.onClientConnectionEvent = plugin.onClientConnectionEvent; 113 | scope.onCommand = plugin.onCommand; 114 | scope.onConnectionEvent = plugin.onConnectionEvent; 115 | scope.onIdle = plugin.onIdle; 116 | scope.unload = function() { 117 | Plugins.unload(filename); 118 | }; 119 | 120 | // call the main function 121 | Plugins.plugins[filename].onLoad.call(scope); 122 | }, 123 | 124 | /** 125 | * 126 | */ 127 | unload: function(filename) { 128 | if(Plugins.plugins[filename] == undefined) { 129 | console.log('Error for unloading `'+ filename +'\': file does not exist.'); 130 | return; 131 | } 132 | 133 | if(Plugins.plugins[filename].onUnload != undefined) { 134 | // call the unload function 135 | Plugins.plugins[filename].onUnload.call(Plugins.API); 136 | } 137 | 138 | // delete from array 139 | delete Plugins.plugins[filename]; 140 | 141 | // delete from require cache 142 | delete require.cache[__dirname.replace('lib', '') + Plugins.PLUGINS_DIR + 143 | filename +'.js']; 144 | }, 145 | 146 | /** 147 | * 148 | */ 149 | addHandlers: function(plugin) { 150 | 151 | plugin.handlers = { 152 | onResultHandler: {}, 153 | onCallHandler: {}, 154 | onClientConnectionEventHandler: null, 155 | onCommandHandler: null, 156 | onConnectionEventHandler: null, 157 | onIdleHandler: null 158 | }; 159 | 160 | plugin.onResult = function(procName, handlerFunc) { 161 | plugin.handlers.onResultHandler[procName] = handlerFunc; 162 | }; 163 | 164 | plugin.onCall = function(procName, handlerFunc) { 165 | plugin.handlers.onCallHandler[procName] = handlerFunc; 166 | }; 167 | 168 | plugin.onClientConnectionEvent = function(handlerFunc) { 169 | plugin.handlers.onClientConnectionEventHandler = handlerFunc; 170 | }; 171 | 172 | plugin.onCommand = function(handlerFunc) { 173 | plugin.handlers.onCommandHandler = handlerFunc; 174 | }; 175 | 176 | plugin.onConnectionEvent = function(handlerFunc) { 177 | plugin.handlers.onConnectionEventHandler = handlerFunc; 178 | }; 179 | 180 | plugin.onIdle = function(handlerFunc) { 181 | plugin.handlers.onIdleHandler = handlerFunc; 182 | }; 183 | }, 184 | 185 | /** 186 | * 187 | */ 188 | isPluginCompliant: function(filename, plugin) { 189 | // entry point 190 | if(plugin.onLoad == undefined) { 191 | console.log('Error when reading the `'+ filename +'\' plugin: '+ 192 | 'no entry point `onLoad\' function found.'); 193 | return false; 194 | } 195 | 196 | return true; 197 | }, 198 | 199 | /** 200 | * 201 | */ 202 | callResultHandlers: function(procName, values, scope) { 203 | try { 204 | for(var pl in Plugins.plugins) { 205 | Plugins.plugins[pl].handlers.onResultHandler[procName] 206 | .call(scope, values); 207 | } 208 | } catch(e) {} 209 | }, 210 | 211 | /** 212 | * 213 | */ 214 | callHandlers: function(procName, values, scope) { 215 | var response; 216 | try { 217 | for(var pl in Plugins.plugins) { 218 | response = Plugins.plugins[pl].handlers.onCallHandler[procName] 219 | .call(scope, values); 220 | } 221 | } catch(e) {} 222 | 223 | return response; 224 | }, 225 | 226 | /** 227 | * 228 | */ 229 | callClientConnectionEventHandlers: function(type, cbId, scope) { 230 | try { 231 | for(var pl in Plugins.plugins) { 232 | Plugins.plugins[pl].handlers.onClientConnectionEventHandler 233 | .call(scope, type, cbId); 234 | } 235 | } catch(e) {} 236 | }, 237 | 238 | /** 239 | * 240 | */ 241 | callCommandHandlers: function(command, scope) { 242 | var ret = false; 243 | try { 244 | for(var pl in Plugins.plugins) { 245 | if(Plugins.plugins[pl].handlers.onCommandHandler.call(scope, command)) 246 | ret = true; 247 | } 248 | } catch(e) {} 249 | 250 | return ret; 251 | }, 252 | 253 | /** 254 | * 255 | */ 256 | callConnectionEventHandlers: function(type, cbId, scope) { 257 | try { 258 | for(var pl in Plugins.plugins) { 259 | Plugins.plugins[pl].handlers.onConnectionEventHandler 260 | .call(scope, type, cbId); 261 | } 262 | } catch(e) {} 263 | }, 264 | 265 | /** 266 | * 267 | */ 268 | callIdleHandlers: function(scope) { 269 | try { 270 | for(var pl in Plugins.plugins) { 271 | Plugins.plugins[pl].handlers.onIdleHandler 272 | .call(scope); 273 | } 274 | } catch(e) {} 275 | }, 276 | 277 | /** 278 | * Escape string arguments. 279 | * Example: {'status': 'Download'} -> {'status': '"Download"'} 280 | * 281 | * This is mandoratory for JSON parsing functiona afterwards. 282 | */ 283 | escapeStringArguments: function(args) { 284 | for(var arg in args) { 285 | if(typeof args[arg] == 'string') { 286 | args[arg] = '"'+ args[arg] +'"'; 287 | } 288 | } 289 | }, 290 | 291 | }; 292 | 293 | module.exports = Plugins; 294 | 295 | -------------------------------------------------------------------------------- /spi/spi.c: -------------------------------------------------------------------------------- 1 | /* SPI testing utility (see copyright beow) 2 | * adapted for use in Python 3 | * by Louis Thiery 4 | * Lots more flexibility and cleanup by Connor Wolf (imaginaryindustries.com) 5 | * 6 | * compile for Python using: "python setup.py build" 7 | * compiled module will be in "./build/lib.linux-armv6l-2.7/spi.so" 8 | * 9 | * SPI testing utility (using spidev driver) 10 | * 11 | * Copyright (c) 2007 MontaVista Software, Inc. 12 | * Copyright (c) 2007 Anton Vorontsov 13 | * 14 | * This program is free software; you can redistribute it and/or modify 15 | * it under the terms of the GNU General Public License as published by 16 | * the Free Software Foundation; either version 2 of the License. 17 | * 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | static void pabort(const char *s) 32 | { 33 | perror(s); 34 | abort(); 35 | } 36 | 37 | static const char *device = "/dev/spidev0.0"; 38 | static uint8_t mode; 39 | static uint8_t bits = 8; 40 | static uint32_t speed = 500000; 41 | static uint16_t delay; 42 | 43 | int ret = 0; 44 | int fd; 45 | 46 | 47 | static PyObject* openSPI(PyObject *self, PyObject *args, PyObject *kwargs) 48 | { 49 | 50 | static char* kwlist[] = {"device", "mode", "bits", "speed", "delay", NULL}; 51 | 52 | // Adding some sort of mode parsing would probably be a nice idea for the future, so you don't have to specify it as a bitfield 53 | // stuffed into an int. 54 | // For the moment the default mode ("0"), will probably work for 99% of people who need a SPI interface, so I'm not working on that 55 | // 56 | 57 | 58 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|siiii:keywords", kwlist, &device, &mode, &bits, &speed, &delay)) 59 | return NULL; 60 | // It's not clearly documented, but it seems that PyArg_ParseTupleAndKeywords basically only modifies the values passed to it if the 61 | // keyword pertaining to that value is passed to the function. As such, the defaults specified by the variable definition are used 62 | // unless you pass a kwd argument. 63 | // Note that there isn't any proper bounds-checking, so if you pass a value that exceeds the variable size, it's just truncated before 64 | // being stuffed into the avasilable space. For example, passing a bits-per-word of 500 gets truncated to 244. Unfortunately, the 65 | // PyArg_ParseTupleAndKeywords function only seems to support ints of 32 bits. 66 | 67 | PyErr_Clear(); 68 | 69 | // printf("Mode: %i, Bits: %i, Speed: %i, Delay: %i\n", mode, bits, speed, delay); 70 | 71 | 72 | 73 | fd = open(device, O_RDWR); 74 | if (fd < 0) 75 | pabort("can't open device"); 76 | 77 | /* 78 | * Setup SPI mode 79 | */ 80 | ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); 81 | if (ret == -1) 82 | pabort("can't set spi mode"); 83 | 84 | ret = ioctl(fd, SPI_IOC_RD_MODE, &mode); 85 | if (ret == -1) 86 | pabort("can't get spi mode"); 87 | 88 | /* 89 | * bits per word 90 | */ 91 | ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); 92 | if (ret == -1) 93 | pabort("can't set bits per word"); 94 | 95 | ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits); 96 | if (ret == -1) 97 | pabort("can't get bits per word"); 98 | 99 | /* 100 | * max speed hz 101 | */ 102 | ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); 103 | if (ret == -1) 104 | pabort("can't set max speed hz"); 105 | 106 | ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed); 107 | if (ret == -1) 108 | pabort("can't get max speed hz"); 109 | 110 | // Stuff the various initilization parameters into a dict, and return that. 111 | // Note that the returned values may not be completely real. It seems that, at least for the speed value, 112 | // the hardware only has several possible settings (250000, 500000, 1000000, etc...) Strangely enough, the 113 | // ioctl for setting the speed *returns the speed you specify*. However, the hardware seems to default to the 114 | // closest avalable value *below* the specified rate. (i.e. you will never get a speed faster then you spec), 115 | // but you may get a slower value. 116 | 117 | //It would probably be a good idea to bin-down the passed arguement to the available values, and return 118 | // that. 119 | 120 | PyObject* retDict; 121 | retDict = PyDict_New(); 122 | 123 | #if PY_MAJOR_VERSION >= 3 124 | PyDict_SetItem(retDict, PyBytes_FromString("mode"), PyLong_FromLong((long)mode)); 125 | PyDict_SetItem(retDict, PyBytes_FromString("bits"), PyLong_FromLong((long)bits)); 126 | PyDict_SetItem(retDict, PyBytes_FromString("speed"), PyLong_FromLong((long)speed)); 127 | PyDict_SetItem(retDict, PyBytes_FromString("delay"), PyLong_FromLong((long)delay)); 128 | #else 129 | PyDict_SetItem(retDict, PyString_FromString("mode"), PyInt_FromLong((long)mode)); 130 | PyDict_SetItem(retDict, PyString_FromString("bits"), PyInt_FromLong((long)bits)); 131 | PyDict_SetItem(retDict, PyString_FromString("speed"), PyInt_FromLong((long)speed)); 132 | PyDict_SetItem(retDict, PyString_FromString("delay"), PyInt_FromLong((long)delay)); 133 | #endif 134 | 135 | 136 | return retDict; 137 | } 138 | 139 | 140 | 141 | static PyObject* transfer(PyObject* self, PyObject* arg) 142 | { 143 | PyObject* transferTuple; 144 | 145 | if(!PyArg_ParseTuple(arg, "O", &transferTuple)) // "O" - Gets non-NULL borrowed reference to Python argument. 146 | return NULL; // As far as I can tell, it's mostly just copying arg[0] into transferTuple 147 | // and making sure at least one arg has been passed (I think) 148 | 149 | if(!PyTuple_Check(transferTuple)) // The only argument we support is a single tuple. 150 | pabort("Only accepts a single tuple as an argument\n"); 151 | 152 | 153 | uint32_t tupleSize = PyTuple_Size(transferTuple); 154 | 155 | uint8_t tx[tupleSize]; 156 | uint8_t rx[tupleSize]; 157 | PyObject* tempItem; 158 | 159 | uint16_t i=0; 160 | 161 | while(i < tupleSize) 162 | { 163 | tempItem = PyTuple_GetItem(transferTuple, i); // 164 | #if PY_MAJOR_VERSION >= 3 165 | if(!PyLong_Check(tempItem)) 166 | #else 167 | if(!PyInt_Check(tempItem)) 168 | #endif 169 | { 170 | pabort("non-integer contained in tuple\n"); 171 | } 172 | #if PY_MAJOR_VERSION >= 3 173 | tx[i] = (uint8_t)PyLong_AsSsize_t(tempItem); 174 | #else 175 | tx[i] = (uint8_t)PyInt_AsSsize_t(tempItem); 176 | #endif 177 | 178 | i++; 179 | 180 | } 181 | 182 | struct spi_ioc_transfer tr = { 183 | .tx_buf = (unsigned long)tx, 184 | .rx_buf = (unsigned long)rx, 185 | .len = tupleSize, 186 | .delay_usecs = delay, 187 | .speed_hz = speed, 188 | .bits_per_word = bits, 189 | .cs_change = 0, 190 | }; 191 | 192 | ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); 193 | if (ret < 1) 194 | pabort("can't send spi message"); 195 | 196 | transferTuple = PyTuple_New(tupleSize); 197 | for(i=0;i= 3 219 | static struct PyModuleDef moduledef = { 220 | PyModuleDef_HEAD_INIT, 221 | "spi", /* m_name */ 222 | "spi library", /* m_doc */ 223 | -1, /* m_size */ 224 | SpiMethods, /* m_methods */ 225 | NULL, /* m_reload */ 226 | NULL, /* m_traverse */ 227 | NULL, /* m_clear */ 228 | NULL, /* m_free */ 229 | }; 230 | #endif 231 | 232 | PyMODINIT_FUNC 233 | 234 | #if PY_MAJOR_VERSION >= 3 235 | PyInit_spi(void) 236 | #else 237 | initspi(void) 238 | #endif 239 | { 240 | #if PY_MAJOR_VERSION >= 3 241 | PyObject *module = PyModule_Create(&moduledef); 242 | #else 243 | (void) Py_InitModule("spi", SpiMethods); 244 | #endif 245 | 246 | #if PY_MAJOR_VERSION >= 3 247 | return module; 248 | #endif 249 | } 250 | -------------------------------------------------------------------------------- /spi/spi_multi.c: -------------------------------------------------------------------------------- 1 | /* SPI testing utility (see copyright beow) 2 | * adapted for use in Python 3 | * by Louis Thiery 4 | * Lots more flexibility and cleanup by Connor Wolf (imaginaryindustries.com) 5 | * 10/2015: handling multiple SPI devices by Markus Schwaiger (lists (at) msedv.at) 6 | * 7 | * compile for Python using: "python setup.py build" 8 | * compiled module will be in "./build/lib.linux-armv6l-2.7/spi.so" 9 | * 10 | * SPI testing utility (using spidev driver) 11 | * 12 | * Copyright (c) 2007 MontaVista Software, Inc. 13 | * Copyright (c) 2007 Anton Vorontsov 14 | * 15 | * This program is free software; you can redistribute it and/or modify 16 | * it under the terms of the GNU General Public License as published by 17 | * the Free Software Foundation; either version 2 of the License. 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | static void pabort(const char *s) 33 | { 34 | perror(s); 35 | abort(); 36 | } 37 | 38 | static uint8_t mode; 39 | static uint8_t bits = 8; 40 | static uint32_t speed = 500000; 41 | static uint16_t delay; 42 | 43 | static PyObject* openSPI(PyObject *self, PyObject *args, PyObject *kwargs) { 44 | int ret = 0; 45 | int fd; 46 | static const char *device = "/dev/spidev0.0"; 47 | static char* kwlist[] = {"device", "mode", "bits", "speed", "delay", NULL}; 48 | 49 | // Adding some sort of mode parsing would probably be a nice idea for the future, so you don't have to specify it as a bitfield 50 | // stuffed into an int. 51 | // For the moment the default mode ("0"), will probably work for 99% of people who need a SPI interface, so I'm not working on that 52 | 53 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|siiii:keywords", kwlist, &device, &mode, &bits, &speed, &delay)) 54 | return NULL; 55 | // It's not clearly documented, but it seems that PyArg_ParseTupleAndKeywords basically only modifies the values passed to it if the 56 | // keyword pertaining to that value is passed to the function. As such, the defaults specified by the variable definition are used 57 | // unless you pass a kwd argument. 58 | // Note that there isn't any proper bounds-checking, so if you pass a value that exceeds the variable size, it's just truncated before 59 | // being stuffed into the avasilable space. For example, passing a bits-per-word of 500 gets truncated to 244. Unfortunately, the 60 | // PyArg_ParseTupleAndKeywords function only seems to support ints of 32 bits. 61 | 62 | PyErr_Clear(); 63 | 64 | // printf("*** SPI.C openSPI: Mode: %i, Bits: %i, Speed: %i, Delay: %i\n", mode, bits, speed, delay); 65 | 66 | fd = open(device, O_RDWR); 67 | if (fd < 0) 68 | pabort("can't open device"); 69 | 70 | // printf("*** SPI.C openSPI: fd: %i\n", fd); 71 | 72 | /* 73 | * Setup SPI mode 74 | */ 75 | ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); 76 | if (ret == -1) 77 | pabort("can't set spi mode"); 78 | 79 | ret = ioctl(fd, SPI_IOC_RD_MODE, &mode); 80 | if (ret == -1) 81 | pabort("can't get spi mode"); 82 | 83 | /* 84 | * bits per word 85 | */ 86 | ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); 87 | if (ret == -1) 88 | pabort("can't set bits per word"); 89 | 90 | ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits); 91 | if (ret == -1) 92 | pabort("can't get bits per word"); 93 | 94 | /* 95 | * max speed hz 96 | */ 97 | ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); 98 | if (ret == -1) 99 | pabort("can't set max speed hz"); 100 | 101 | ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed); 102 | if (ret == -1) 103 | pabort("can't get max speed hz"); 104 | 105 | // Stuff the various initilization parameters into a dict, and return that. 106 | // Note that the returned values may not be completely real. It seems that, at least for the speed value, 107 | // the hardware only has several possible settings (250000, 500000, 1000000, etc...) Strangely enough, the 108 | // ioctl for setting the speed *returns the speed you specify*. However, the hardware seems to default to the 109 | // closest avalable value *below* the specified rate. (i.e. you will never get a speed faster then you spec), 110 | // but you may get a slower value. 111 | 112 | //It would probably be a good idea to bin-down the passed arguement to the available values, and return 113 | // that. 114 | 115 | PyObject* retDict; 116 | retDict = PyDict_New(); 117 | 118 | #if PY_MAJOR_VERSION >= 3 119 | PyDict_SetItem(retDict, PyBytes_FromString("fd"), PyLong_FromLong((long)fd)); 120 | PyDict_SetItem(retDict, PyBytes_FromString("mode"), PyLong_FromLong((long)mode)); 121 | PyDict_SetItem(retDict, PyBytes_FromString("bits"), PyLong_FromLong((long)bits)); 122 | PyDict_SetItem(retDict, PyBytes_FromString("speed"), PyLong_FromLong((long)speed)); 123 | PyDict_SetItem(retDict, PyBytes_FromString("delay"), PyLong_FromLong((long)delay)); 124 | #else 125 | PyDict_SetItem(retDict, PyString_FromString("fd"), PyInt_FromLong((long)fd)); 126 | PyDict_SetItem(retDict, PyString_FromString("mode"), PyInt_FromLong((long)mode)); 127 | PyDict_SetItem(retDict, PyString_FromString("bits"), PyInt_FromLong((long)bits)); 128 | PyDict_SetItem(retDict, PyString_FromString("speed"), PyInt_FromLong((long)speed)); 129 | PyDict_SetItem(retDict, PyString_FromString("delay"), PyInt_FromLong((long)delay)); 130 | #endif 131 | 132 | return retDict; 133 | } 134 | 135 | static PyObject* transfer(PyObject* self, PyObject* arg) { 136 | int ret = 0; 137 | int fd; 138 | PyObject* transferTuple; 139 | 140 | // "O" - Gets non-NULL borrowed reference to Python argument. 141 | // As far as I can tell, it's mostly just copying arg[0] into transferTuple 142 | // and making sure at least one arg has been passed (I think) 143 | if(!PyArg_ParseTuple(arg, "iO", &fd, &transferTuple)) 144 | return NULL; 145 | 146 | // printf("*** SPI.C transfer: fd: %i\n", fd); 147 | 148 | // The only argument we support is a single tuple. 149 | if(!PyTuple_Check(transferTuple)) 150 | pabort("Only accepts a single tuple as an argument\n"); 151 | 152 | uint32_t tupleSize = PyTuple_Size(transferTuple); 153 | 154 | uint8_t tx[tupleSize]; 155 | uint8_t rx[tupleSize]; 156 | PyObject* tempItem; 157 | 158 | uint16_t i=0; 159 | 160 | while(i < tupleSize) 161 | { 162 | tempItem = PyTuple_GetItem(transferTuple, i); // 163 | #if PY_MAJOR_VERSION >= 3 164 | if(!PyLong_Check(tempItem)) 165 | #else 166 | if(!PyInt_Check(tempItem)) 167 | #endif 168 | { 169 | pabort("non-integer contained in tuple\n"); 170 | } 171 | #if PY_MAJOR_VERSION >= 3 172 | tx[i] = (uint8_t)PyLong_AsSsize_t(tempItem); 173 | #else 174 | tx[i] = (uint8_t)PyInt_AsSsize_t(tempItem); 175 | #endif 176 | 177 | i++; 178 | 179 | } 180 | 181 | struct spi_ioc_transfer tr = { 182 | .tx_buf = (unsigned long)tx, 183 | .rx_buf = (unsigned long)rx, 184 | .len = tupleSize, 185 | .delay_usecs = delay, 186 | .speed_hz = speed, 187 | .bits_per_word = bits, 188 | .cs_change = 1, 189 | }; 190 | 191 | ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); 192 | if (ret < 1) 193 | pabort("can't send spi message"); 194 | 195 | transferTuple = PyTuple_New(tupleSize); 196 | 197 | for (i = 0; i < tupleSize; i++) 198 | PyTuple_SetItem(transferTuple, i, Py_BuildValue("i",rx[i])); 199 | 200 | return transferTuple; 201 | } 202 | 203 | static PyObject* closeSPI (PyObject* self, PyObject* arg) { 204 | int fd; 205 | 206 | if(!PyArg_ParseTuple(arg, "i", &fd)) 207 | return NULL; 208 | 209 | // printf ("*** SPI.C closeSPI: fd: %i\n", fd); 210 | PyErr_Clear(); 211 | 212 | close (fd); 213 | Py_RETURN_NONE; 214 | } 215 | 216 | static PyMethodDef SpiMethods[] = { 217 | {"openSPI", (PyCFunction)openSPI, METH_VARARGS | METH_KEYWORDS, "Open SPI Port."}, 218 | {"transfer", (PyCFunction)transfer, METH_VARARGS, "Transfer data."}, 219 | {"closeSPI", (PyCFunction)closeSPI, METH_VARARGS, "Close SPI port."}, 220 | {NULL, NULL, 0, NULL} 221 | }; 222 | 223 | #if PY_MAJOR_VERSION >= 3 224 | static struct PyModuleDef moduledef = { 225 | PyModuleDef_HEAD_INIT, 226 | "spi", /* m_name */ 227 | "spi library", /* m_doc */ 228 | -1, /* m_size */ 229 | SpiMethods, /* m_methods */ 230 | NULL, /* m_reload */ 231 | NULL, /* m_traverse */ 232 | NULL, /* m_clear */ 233 | NULL, /* m_free */ 234 | }; 235 | #endif 236 | 237 | PyMODINIT_FUNC 238 | 239 | #if PY_MAJOR_VERSION >= 3 240 | PyInit_spi(void) 241 | #else 242 | initspi(void) 243 | #endif 244 | { 245 | #if PY_MAJOR_VERSION >= 3 246 | PyObject *module = PyModule_Create(&moduledef); 247 | #else 248 | (void) Py_InitModule("spi", SpiMethods); 249 | #endif 250 | ; 251 | 252 | #if PY_MAJOR_VERSION >= 3 253 | return module; 254 | #endif 255 | } 256 | -------------------------------------------------------------------------------- /ocpp/lib/soap/lib/server.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Vinay Pulim 3 | * MIT Licensed 4 | */ 5 | 6 | function findKey(obj, val) { 7 | for (var n in obj) if (obj[n] === val) return n; 8 | } 9 | 10 | var url = require('url'), 11 | compress = null; 12 | 13 | try { compress = require("compress"); } catch(e) {} 14 | 15 | var Server = function(server, path, services, wsdl) { 16 | var self = this, 17 | listeners = server.listeners('request'); 18 | 19 | this.services = services; 20 | this.wsdl = wsdl; 21 | if (path[path.length-1] != '/') path += '/'; 22 | wsdl.onReady(function(err) { 23 | server.removeAllListeners('request'); 24 | server.addListener('request', function(req, res) { 25 | if (typeof self.authorizeConnection === 'function') { 26 | if (!self.authorizeConnection(req.connection.remoteAddress)) { 27 | res.end(); 28 | return; 29 | } 30 | } 31 | var reqPath = url.parse(req.url).pathname; 32 | if (reqPath[reqPath.length-1] != '/') reqPath += '/'; 33 | if (path === reqPath) { 34 | self._requestListener(req, res); 35 | } 36 | else { 37 | for (var i = 0, len = listeners.length; i < len; i++){ 38 | listeners[i].call(this, req, res); 39 | } 40 | } 41 | }); 42 | }) 43 | } 44 | 45 | // XXX modif lib: check error function 46 | Server.prototype.checkError = function(result) { 47 | 48 | }; 49 | 50 | // XXX modif lib: check error function 51 | Server.prototype._returnError = function(result) { 52 | 53 | }; 54 | 55 | Server.prototype._requestListener = function(req, res) { 56 | var self = this; 57 | if (req.method === 'GET') { 58 | var search = url.parse(req.url).search; 59 | if (search && search.toLowerCase() === '?wsdl') { 60 | res.setHeader("Content-Type", "application/xml"); 61 | res.write(self.wsdl.toXML()); 62 | } 63 | res.end(); 64 | } 65 | else if (req.method === 'POST') { 66 | res.setHeader('Content-Type', req.headers['content-type']); 67 | var chunks = [], gunzip; 68 | if (compress && req.headers["content-encoding"] == "gzip") { 69 | gunzip = new compress.Gunzip; 70 | gunzip.init(); 71 | } 72 | req.on('data', function(chunk) { 73 | if (gunzip) chunk = gunzip.inflate(chunk, "binary"); 74 | chunks.push(chunk); 75 | }) 76 | req.on('end', function() { 77 | var xml = chunks.join(''), result; 78 | if (gunzip) { 79 | gunzip.end(); 80 | gunzip = null; 81 | } 82 | try { 83 | self.log(xml, 'in'); 84 | 85 | // XXX modif lib: for checking errors 86 | if(!self.checkErrors(xml, res)) { 87 | self._process(xml, req.url, function(result) { 88 | res.write(result); 89 | res.end(); 90 | 91 | self.log(result, 'out'); 92 | }); 93 | } 94 | } 95 | catch(err) { 96 | console.log(err) 97 | err = err.stack || err; 98 | res.write(err); 99 | res.end(); 100 | if (typeof self.log === 'function') { 101 | self.log("error", err); 102 | } 103 | } 104 | }); 105 | } 106 | else { 107 | res.end(); 108 | } 109 | } 110 | 111 | // XXX modif lib 112 | Server.prototype.setRemoteAddress = function(address) { 113 | // to be overridden 114 | }; 115 | 116 | // XXX modif lib 117 | Server.prototype.postProcess = function(address) { 118 | // to be overridden 119 | }; 120 | 121 | Server.prototype._process = function(input, URL, callback) { 122 | var self = this, 123 | pathname = url.parse(URL).pathname.replace(/\/$/,''), 124 | obj = this.wsdl.xmlToObject(input), 125 | body = obj.Body, 126 | bindings = this.wsdl.definitions.bindings, binding, 127 | methods, method, methodName, 128 | serviceName, portName; 129 | 130 | if (typeof self.authenticate === 'function') { 131 | if (obj.Header == null || obj.Header.Security == null) { 132 | throw new Error('No security header'); 133 | } 134 | if (!self.authenticate(obj.Header.Security)) { 135 | throw new Error('Invalid username or password'); 136 | } 137 | } 138 | 139 | // XXX modif lib: get remote address 140 | self.setRemoteAddress(obj.Header.chargeBoxIdentity, 141 | obj.Header.From && obj.Header.From.Address, 142 | obj.Header.Action.slice(1)); 143 | 144 | // XXX modif lib: for adding chargeboxidentity header 145 | this.cbId = obj.Header.chargeBoxIdentity; 146 | this.action = obj.Header.Action; 147 | 148 | // use port.location and current url to find the right binding 149 | binding = (function(self){ 150 | var services = self.wsdl.definitions.services; 151 | for(serviceName in services) { 152 | var service = services[serviceName]; 153 | var ports = service.ports; 154 | for(portName in ports) { 155 | var port = ports[portName]; 156 | var portPathname = url.parse(port.location).pathname.replace(/\/$/,''); 157 | // XXX modif lib: permit customized endpoint URL 158 | //if(portPathname===pathname) 159 | return port.binding; 160 | } 161 | } 162 | })(this); 163 | 164 | methods = binding.methods; 165 | 166 | // XXX modif lib: always rpc 167 | // if(binding.style === 'rpc') { 168 | // XXX end 169 | methodName = Object.keys(body)[0]; 170 | 171 | body = body[methodName]; 172 | 173 | // XXX modif lib: OCPP Command = Command / CommandResponse 174 | methodName = methodName.replace('Request',''); 175 | methodName = methodName.charAt(0).toUpperCase() + methodName.slice(1); 176 | // XXX end 177 | 178 | self._executeMethod({ 179 | serviceName: serviceName, 180 | portName: portName, 181 | methodName: methodName, 182 | outputName: methodName + 'Response', 183 | // XXX modif lib: body instead of body[methodName] 184 | args: body, 185 | style: 'rpc' 186 | }, callback); 187 | 188 | self.postProcess(); 189 | 190 | /* XXX modif lib: always use RPC 191 | } else { 192 | var messageElemName = Object.keys(body)[0]; 193 | var pair = binding.topElements[messageElemName]; 194 | self._executeMethod({ 195 | serviceName: serviceName, 196 | portName: portName, 197 | methodName: pair.methodName, 198 | outputName: pair.outputName, 199 | args: body[messageElemName], 200 | style: 'document' 201 | }, callback); 202 | } 203 | */ 204 | 205 | } 206 | 207 | Server.prototype._executeMethod = function(options, callback) { 208 | options = options || {}; 209 | var self = this, 210 | method, body, 211 | serviceName = options.serviceName, 212 | portName = options.portName, 213 | methodName = options.methodName, 214 | outputName = options.outputName, 215 | args = options.args, 216 | style = options.style, 217 | handled = false; 218 | 219 | try { 220 | method = this.services[serviceName][portName][methodName]; 221 | } catch(e) { 222 | return callback(this._envelope('')); 223 | } 224 | 225 | function handleResult(result) { 226 | if (handled) return; 227 | handled = true; 228 | 229 | if(style==='rpc') { 230 | body = self.wsdl.objectToRpcXML(outputName, result, '', self.wsdl.definitions.$targetNamespace); 231 | } else { 232 | var element = self.wsdl.definitions.services[serviceName].ports[portName].binding.methods[methodName].output; 233 | body = self.wsdl.objectToDocumentXML(outputName, result, element.targetNSAlias, element.targetNamespace); 234 | } 235 | callback(self._envelope(body)); 236 | } 237 | 238 | /// XXX modif lib: forward request body 239 | var result = method(args, handleResult); 240 | if (typeof result !== 'undefined') { 241 | handleResult(result); 242 | } 243 | } 244 | 245 | Server.prototype._envelope = function(body) { 246 | var defs = this.wsdl.definitions, 247 | ns = defs.$targetNamespace, 248 | encoding = '', 249 | alias = findKey(defs.xmlns, ns), 250 | // XXX modif lib: add chargeboxidentity header 251 | cbIdHeader = this.cbId ? 252 | ""+ 253 | this.cbId +"" 254 | : "", 255 | actionHeader = this.action ? 256 | ""+ 257 | this.action +"Response" 258 | : ""; 259 | 260 | var xml = "" + 261 | "' + 264 | ""+ cbIdHeader + actionHeader +"" + 265 | "" + 266 | body + 267 | "" + 268 | ""; 269 | return xml; 270 | } 271 | 272 | exports.Server = Server; 273 | -------------------------------------------------------------------------------- /ocpp/lib/simulators.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | /** 4 | * 5 | * 6 | * 7 | */ 8 | 9 | 10 | var OCPP = require('./ocpp-protocol.js'), 11 | Utils = require('./utils.js'), 12 | Transport = require('./transport.js'), 13 | Plugins = require('./plugins.js'); 14 | 15 | /** 16 | * Central System Simulator constructor. 17 | * 18 | * - {CentralSystemSimulator} implements an OCPP Central System. 19 | * Constructor options: TCP port. 20 | * When started, a CentralSystemSimulator creates a {WebSocketServer} on the 21 | * specified port. 22 | * When the WebSocketServer receives a connection request, a 23 | * {WebSocketConnection} is created (by the websocket lib). 24 | * The CentralSystemSimulator uses this connection to create: 25 | * - a {SRPCServerConnection}, to handle RPC calls 26 | * - a {SRPCClientConnection}, to emit RPC calls 27 | * 28 | * @param {int} Port 29 | * @return {CentralSystemSimulator} 30 | * @api public 31 | */ 32 | 33 | function CentralSystemSimulator(port, transport) { 34 | this.port = port; 35 | this._wsServer = null; 36 | 37 | this._connections = {}; 38 | 39 | Plugins.setAPIFields(transport, 'cs', OCPP.SUB_PROTOCOL); 40 | 41 | this.transportLayer = new Transport.TransportLayerServer(this, 42 | transport, 'cs', 'server'); 43 | 44 | // lib fonction redefinitions 45 | // TODO move this to transport.js, can't at the moment because of circular 46 | // require 47 | var _this = this; 48 | if(transport == 'soap') { 49 | this.transportLayer.layer.soapServ.setRemoteAddress = 50 | function(cbId, address, action) { 51 | // if not bootnotification, stop 52 | if(action != 'BootNotification') 53 | return; 54 | 55 | Plugins.callClientConnectionEventHandlers('connected', cbId, this); 56 | 57 | Utils.log('ChargePoint #'+ cbId +' connected.', 'cs'); 58 | _this._connections[cbId] = { 59 | client: new Transport.TransportLayerClient(this, 60 | transport, 'cs', 'client', { fromHeader: address }).layer 61 | }; 62 | }; 63 | 64 | this.transportLayer.layer.soapServ.log = logSoap; 65 | this.transportLayer.layer.soapServ.postProcess 66 | = function() { Plugins.callIdleHandlers(this); }; 67 | } 68 | }; 69 | 70 | CentralSystemSimulator.prototype = { 71 | 72 | /** 73 | * Stop the CentralSystem 74 | * 75 | 76 | stop: function() { 77 | this._wsServer.closeAllConnections(); 78 | this._wsServer.shutDown(); 79 | this._wsServer = null; 80 | 81 | this._httpServer.close(); 82 | this._httpServer = null; 83 | }, 84 | */ 85 | 86 | /* 87 | * Calls a remote procedure 88 | * 89 | * @param {Number} the client ID 90 | * @param {String} the procedure URI 91 | * @api public 92 | */ 93 | remoteAction: function(clientId, procName, args) { 94 | var prot = Utils.retrieveVersion(OCPP.SUB_PROTOCOL); 95 | 96 | if(this._connections[clientId] == undefined) { 97 | Utils.log("Error: charge point #"+ clientId +" does not exist"); 98 | return; 99 | } 100 | 101 | var resultFunction = function(){}; 102 | 103 | if(procName != '') { 104 | if(OCPP.procedures[prot]['cp'][procName] != undefined 105 | && OCPP.procedures[prot]['cp'][procName].resultFunction != undefined) { 106 | resultFunction = OCPP.procedures[prot]['cp'][procName].resultFunction; 107 | } 108 | } 109 | 110 | this._connections[clientId].client.rpcCall(procName, args || {}, 111 | OCPP.TIMEOUT, resultFunction, {to: "cp#" + clientId}); 112 | } 113 | 114 | }; 115 | 116 | /** 117 | * Charge Point Simulator constructor. 118 | * 119 | * - {ChargePointSimulator} implements an OCPP Charge Point. 120 | * Constructor options: server URL. 121 | * A ChargePointSimulator permanently trys to connect to its CentralSystem, 122 | * with a {WebSocketClient}. On successfull connection, a 123 | * {WebSocketConnection} is created (by the websocket lib). 124 | * The ChargePointSimulator uses this connection to create: 125 | * - a {SRPCServerConnection}, to handle RPC calls 126 | * - a {SRPCClientConnection}, to emit RPC calls 127 | * 128 | * @param {String} URI 129 | * @return {ChargePointSimulator} 130 | * @api public 131 | */ 132 | 133 | function ChargePointSimulator(uri, identifier, protocol, transport, 134 | soapOptions) { 135 | this.uri = uri; 136 | this.protocol = protocol || "ocpp1.5"; 137 | this.transport = transport; 138 | 139 | this.chargePointId = identifier; 140 | this.clientConnection = null; 141 | 142 | Plugins.setAPIFields(transport, 'cp', OCPP.SUB_PROTOCOL, this.chargePointId); 143 | 144 | this.transportLayer = new Transport.TransportLayerClient(this, 145 | transport, 'cp', 'client', soapOptions); 146 | 147 | if(this.transport == 'soap') { 148 | this.transportLayer.layer.soapServ.log = logSoap; 149 | this.transportLayer.layer.soapServ.postProcess 150 | = function() { Plugins.callIdleHandlers(this); }; 151 | } 152 | }; 153 | 154 | ChargePointSimulator.prototype = { 155 | 156 | /** 157 | * Calls a client procedure 158 | * 159 | * @param {String} Procedure URI 160 | * @param {Array} Arguments 161 | */ 162 | clientAction: function(procUri, args) { 163 | var resultFunction = function(){}, 164 | version = Utils.retrieveVersion(this.transportLayer.simulator.protocol); 165 | 166 | if(OCPP.procedures[version]['cs'][procUri] != undefined 167 | && OCPP.procedures[version]['cs'][procUri].resultFunction != undefined) 168 | resultFunction = OCPP.procedures[version]['cs'][procUri].resultFunction; 169 | 170 | if(this.clientConnection) 171 | this.clientConnection.rpcCall(procUri, args, OCPP.TIMEOUT, 172 | resultFunction, {to: "cs"}); 173 | else 174 | console.log('Error: not connected to any central system.'); 175 | } 176 | 177 | }; 178 | 179 | /** 180 | * 181 | * 182 | */ 183 | function commandCPtoCS(cpId, procName, args) { 184 | var prot = Utils.retrieveVersion(OCPP.SUB_PROTOCOL); 185 | 186 | 187 | if(cpId == -1) { 188 | cpId = Utils.getId(Simulators.chargePoints); 189 | 190 | if(cpId == -1) { 191 | console.log("Error: cannot use shortened command : there is no or more"+ 192 | " than one charge point connected."); 193 | return; 194 | } 195 | } 196 | 197 | if(Simulators.chargePoints[cpId] == undefined) { 198 | Utils.log("Error: charge point #"+ cpId +" does not exist."); 199 | return; 200 | } 201 | 202 | // specific case: WebSocket Ping 203 | if(procName == "WebSocketPing") { 204 | if(Simulators.chargePoints[cpId].clientConnection != undefined) 205 | Simulators.chargePoints[cpId].clientConnection._connection._connection 206 | .ping(); 207 | return; 208 | } 209 | 210 | // if charge point protocol is 1.2 211 | if(prot == '1.2') { 212 | if(OCPP.wsdl[prot][procName + 'Request'] == undefined) { 213 | console.log("Error: procedure `"+ procName +"' is not available for a"+ 214 | " OCPP 1.2 Charge Point Simulator"); 215 | return; 216 | } 217 | } 218 | 219 | // if send or send_raw 220 | var values = typeof args == 'string' ? 221 | args : 222 | OCPP.getRequestValues(procName, args); 223 | 224 | // if error 225 | if(values == -1) 226 | return; 227 | 228 | 229 | Simulators.chargePoints[cpId].clientAction(procName, values); 230 | }; 231 | 232 | 233 | /** 234 | * 235 | * 236 | */ 237 | function commandCStoCP(cpId, procName, args) { 238 | var prot = Utils.retrieveVersion(OCPP.SUB_PROTOCOL); 239 | 240 | if(cpId == -1) { 241 | if (Simulators.centralSystem != null) 242 | cpId = Utils.getId(Simulators.centralSystem._connections); 243 | 244 | if(cpId == -1) { 245 | console.log("Error: cannot use shortened command : there is no or more"+ 246 | " than one charge point connected."); 247 | return; 248 | } 249 | } 250 | 251 | // if cp does not exit 252 | if(Simulators.centralSystem._connections[cpId] == undefined) { 253 | Utils.log("Error: remote charge point #"+ cpId +" does not exist."); 254 | return; 255 | } 256 | 257 | // if charge point protocol is 1.2 258 | if(prot == '1.2') { 259 | if(OCPP.wsdl[prot][procName + 'Request'] == undefined) { 260 | console.log("Error: procedure `"+ procName +"' is not available for a"+ 261 | " OCPP 1.2 Charge Point Simulator"); 262 | return; 263 | } 264 | } 265 | 266 | // specific case: WebSocket Ping 267 | if(procName == "WebSocketPing") { 268 | if(Simulators.centralSystem._connections[cpId].client != undefined) 269 | Simulators.centralSystem._connections[cpId] 270 | .client._connection._connection.ping(); 271 | return; 272 | } 273 | 274 | var values = OCPP.getRequestValues(procName, args); 275 | 276 | // if error 277 | if(values == -1) 278 | return; 279 | 280 | Simulators.centralSystem.remoteAction(cpId, procName, values); 281 | }; 282 | 283 | 284 | /** 285 | * 286 | * 287 | */ 288 | 289 | function logSoap(xml, direction) { 290 | // @scope: soap server 291 | var direction = direction || 'in', 292 | prefix = direction == 'in' ? '<<' : '>>', 293 | rawContent = xml, 294 | content = this.wsdl.xmlToObject(xml), 295 | from = null, 296 | action = null; 297 | 298 | // if no content then do nothing 299 | if(!content) { 300 | if(direction == 'in') 301 | Utils.log('< '+ 66 | 'for simulating a connection interruption.'); 67 | return true; 68 | } 69 | 70 | var connectorId = parseInt(commandObj.arguments[0]), 71 | isLiaisonWorking = !!(commandOjb.arguments[1] == 'on'); 72 | 73 | for(var connector in cbxs.connectors) { 74 | if(connector.connectorId == connectorId) { 75 | connector.isLiaisonWorking = isLiaisonWorking; 76 | } 77 | } 78 | 79 | return true; 80 | } 81 | 82 | if(commandObj.command == 'identification') { 83 | if(commandObj.arguments.length == 0) { 84 | console.log(cbxs.logPrefix +'Usage: identification '); 85 | return true; 86 | } 87 | 88 | var id = commandObj.arguments[0]; 89 | 90 | // check if connector related to id 91 | for(var index in cbxs.connectors) { 92 | var connector = cbxs.connectors[index]; 93 | if(connector.idTagRelated == id) { 94 | cbxs.actionsQueue.push({ 95 | procedure: 'StopTransaction', 96 | arguments: {idTag: id} 97 | }); 98 | return true; 99 | } 100 | } 101 | 102 | var found = false; 103 | for(var key in cbxs.whiteList) { 104 | if(key == id) 105 | found = true; 106 | } 107 | 108 | if(found) { 109 | var connector = cbxs.getAvailableConnector(); 110 | connector.idTagRelated = id; 111 | cbxs.startTransactionIfPossible(connector.connectorId); 112 | } 113 | else { 114 | cbxs.idTagTmp = id; 115 | self.cp.call('Authorize', {idTag: id}); 116 | } 117 | 118 | return true; 119 | } 120 | }); 121 | 122 | /** 123 | * what to do when receiving a call result: 124 | */ 125 | self.onResult('BootNotification', function(values) { 126 | cbxs.heartbeatInterval = values.heartbeatInterval; 127 | cbxs.sendHB(self, true); 128 | }); 129 | 130 | self.onResult('Authorize', function(values) { 131 | if(values.idTagInfo.status == "Accepted") { 132 | var connector = cbxs.getAvailableConnector(); 133 | connector.isCharging = true; 134 | connector.transactionIdClient = cbxs.transactionIdClient++; 135 | connector.idTagRelated = cbxs.idTagTmp; 136 | cbxs.idTagTmp = null; 137 | cbxs.startTransactionIfPossible(connector.connectorId); 138 | } 139 | }); 140 | 141 | self.onResult('StartTransaction', function(values) { 142 | var transactionId = values.transactionId; 143 | 144 | for(var index in cbxs.connectors) { 145 | var connector = cbxs.connectors[index]; 146 | if(connector.isCharging && connector.transactionIdServer == null) { 147 | connector.transactionIdServer = transactionId; 148 | } 149 | } 150 | }); 151 | 152 | /** 153 | * when a remote call is received: 154 | */ 155 | 156 | self.onCall('ClearCache', function(values) { 157 | // clear the white list 158 | cbxs.whiteList = []; 159 | self.log(cbxs.logPrefix + ' White list cleared.'); 160 | }); 161 | 162 | self.onCall('ChangeAvailability', function(values) { 163 | for(var index in cbxs.connectors) { 164 | var connector = cbxs.connectors[index]; 165 | if(connector.connectorId == values.connectorId) { 166 | connector.isBlocked = !!(values.type == "Operative"); 167 | } 168 | } 169 | }); 170 | 171 | self.onCall('RemoteStartTransaction', function(values) { 172 | var connector = cbxs.getConnectorFromConnectorId(values.connectorId); 173 | 174 | if(connector.isCharging && !connector.isBlocked 175 | && connector.isLiaisonWorking) { 176 | connector.idTagRelation = values.idTag; 177 | cbxs.startTransactionIfPossible(values.connectorId); 178 | } 179 | else { 180 | return { status: "Rejected" }; 181 | } 182 | }); 183 | 184 | self.onCall('RemoteStopTransaction', function(values) { 185 | var found = false; 186 | 187 | for(var index in cbxs.connectors) { 188 | var connector = cbxs.connectors[index]; 189 | if(connector.transactionIdServer == values.transactionId) { 190 | connector.isCharging = false; 191 | connector.transactionIdClient = null; 192 | connector.transactionIdServer = null; 193 | 194 | found = true; 195 | } 196 | } 197 | 198 | if (found) { 199 | cbxs.actionsQueue.push({ 200 | procedure: 'StopTransaction', 201 | arguments: { 202 | transactionId: values.transactionId 203 | } 204 | }); 205 | } 206 | else { 207 | return { status: "Rejected" }; 208 | } 209 | }); 210 | 211 | self.onCall('GetDiagnostics', function(values) { 212 | self.cp.call('DiagnosticsStatusNotification', {"status": "Uploaded"}); 213 | }); 214 | 215 | self.onCall('UpdateFirmware', function(values) { 216 | self.cp.call('FirmwareStatusNotification', {"status": "Downloaded"}); 217 | self.cp.call('FirmwareStatusNotification', {"status": "Installed"}); 218 | }); 219 | 220 | self.onIdle(function() { 221 | cbxs.processActionsQueue(); 222 | 223 | clearTimeout(cbxs.hbTimeout); 224 | cbxs.sendHB(self, true); 225 | }); 226 | }, 227 | 228 | /** 229 | * Customs fields 230 | */ 231 | 232 | self: null, 233 | 234 | logPrefix: '[CBXS] ', 235 | 236 | whiteList: [], 237 | actionsQueue: [], 238 | transactionsQueue: [], 239 | 240 | hbTimeout: null, 241 | heartbeatInterval: null, 242 | 243 | idTagTmp: null, 244 | 245 | transactionIdClient: 0, 246 | 247 | connectors: [ 248 | { 249 | connectorId: 1, 250 | isCharging: false, 251 | isBlocked: false, 252 | isLiaisonWorking: true, 253 | idTagRelated: "", 254 | 255 | transactionIdClient: null, // generated by client 256 | transactionIdServer: null, // response from server 257 | },{ 258 | connectorId: 2, 259 | isCharging: false, 260 | isBlocked: false, 261 | isLiaisonWorking: true, 262 | idTagRelated: "", 263 | 264 | transactionIdClient: null, // generated by client 265 | transactionIdServer: null, // response from server 266 | } 267 | ], 268 | 269 | processActionsQueue: function() { 270 | var msg = null; 271 | while(msg = cbxs.actionsQueue.pop()) { 272 | switch(msg.procedure) { 273 | case 'StartTransaction': 274 | var connector 275 | = cbxs.getConnectorFromConnectorId(msg.arguments.connectorId); 276 | if(!connector.isBlocked && connector.isLiaisonWorking) { 277 | cbxs.transactionsQueue.push(msg); 278 | cbxs.processTransactionsQueue(); 279 | } 280 | break; 281 | default: 282 | cbxs.transactionsQueue.push(msg); 283 | cbxs.processTransactionsQueue(); 284 | } 285 | } 286 | }, 287 | 288 | processTransactionsQueue: function() { 289 | var msg = null; 290 | while(msg = cbxs.transactionsQueue.pop()) { 291 | cbxs.self.cp.call(msg.procedure, msg.arguments); 292 | } 293 | }, 294 | 295 | sendHB: function(self, dropFirst) { 296 | if(!cbxs.heartbeatInterval) 297 | return; 298 | 299 | if(!dropFirst) 300 | self.cp.call('Heartbeat'); 301 | 302 | cbxs.hbTimeout = setTimeout(cbxs.sendHB, cbxs.heartbeatInterval * 1000, 303 | self); 304 | }, 305 | 306 | startTransactionIfPossible: function(connectorId) { 307 | var connector = null; 308 | for(var index in cbxs.connectors) { 309 | var c = cbxs.connectors[index]; 310 | if(c.connectorId == connectorId) 311 | connector = c; 312 | } 313 | 314 | if(connector == null) 315 | return; 316 | 317 | if(!connector.isBlocked && connector.isLiaisonWorking) { 318 | cbxs.actionsQueue.push({ 319 | procedure: 'StartTransaction', 320 | arguments: { 321 | connectorId: connectorId, 322 | idTag: connector.idTagRelated, 323 | timestamp: new Date().toISOString(), 324 | meterStart: 0, 325 | reservationId: 0 326 | } 327 | }); 328 | } 329 | else { 330 | cbxs.self.log(cbxs.logPrefix + "Can't start transaction on connector #"+ 331 | connectorId); 332 | } 333 | }, 334 | 335 | getAvailableConnector: function() { 336 | for(var index in cbxs.connectors) { 337 | var connector = cbxs.connectors[index]; 338 | if(!connector.isCharging) 339 | return connector; 340 | } 341 | 342 | return null; 343 | }, 344 | 345 | getConnectorFromConnectorId: function(connectorId) { 346 | for(var index in cbxs.connectors) { 347 | var c = cbxs.connectors[index]; 348 | if(c.connectorId == connectorId) 349 | return c; 350 | } 351 | 352 | return null; 353 | } 354 | 355 | }; 356 | 357 | module.exports = cbxs; 358 | 359 | -------------------------------------------------------------------------------- /ocpp/plugins/getting_started_with_plugins.html: -------------------------------------------------------------------------------- 1 |

Getting Started With Plugins

2 | 3 |

OCPPJS 1.0.0 introduces a new feature : the plugins. This document is here for helping developers to write plugins for the simulator. It also contains the API documentation.

4 | 5 |

Writing plugins allow yourself to define the behavior of the simulator without modifying the source code of the program. A plugin consists of one JavaScript file which can be loaded by the simulator.

6 | 7 |

API Documentation

8 | 9 |

Input/Output API

10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 24 | 26 | 27 |
FunctionExample
log(message)
18 | Display message to screen.
log("Plugin Started !");
parse(line)
23 | Parse a raw command line to an object.
parse('show cache'); 
 25 |         => {command: 'show', arguments: ['cache']};
28 | 29 |

Simulator API

30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 39 | 40 | 41 | 42 | 44 | 45 | 46 |
FunctionExample
cp.call(procName, args)
38 | Process a RPC call from charge point to central system.
cp.call('BootNotification');
cs.call(procName, args)
43 | Process a remote RPC call from a central system to a charge point.
cs.call('ClearCache');
47 | 48 |

Callback API

49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 58 | 65 | 66 | 67 | 69 | 76 | 77 | 78 | 80 | 90 | 91 | 92 | 94 | 104 | 105 | 106 | 108 | 118 | 119 | 120 | 122 | 129 | 130 |
FunctionExample
onResult(procName, handlerFunc)
57 | Trigger the handlerFunc function when a call result for the procName procedure is received.
59 |
 60 | onResult('BootNotification', function(values)   {
 61 |   log('Heartbeat interval: '+ values.heartbeatInterval);
 62 | });
 63 |         
64 |
onCall(procName, handlerFunc)
68 | Trigger the handlerFunc function when a call message for the procName procedure is received.
70 |
 71 | onCall('ClearCache', function(values) {
 72 |   // clear the cache
 73 | });
 74 |         
75 |
onConnectionEvent(handlerFunc)
79 | For a Charge Point simulator, trigger the handlerFunc function when a new connection event occurs. Event: connected, disconnected
81 |
 82 | onConnectionEvent(function(type, cpId) {
 83 |   if(type == 'connected')
 84 |     log('Connected to Central System.');
 85 |   else if (type == 'disconnected')
 86 |     log('Disconnected to Central System.');
 87 | });
 88 |         
89 |
onClientConnectionEvent(handlerFunc)
93 | For a Central System. simulator, trigger the handlerFunc function when a new connection event occurs. Event: connected, disconnected
95 |
 96 | onClientConnectionEvent(function(type, cpId) {
 97 |   if(type == 'connected')
 98 |     log('Connected to Central System.');
 99 |   else if (type == 'disconnected')
100 |     log('Disconnected to Central System.');
101 | });
102 |         
103 |
onCommand(handlerFunc)
107 | Trigger the handlerFunc function when a new command is entered on the command line interface. If the handlerFunc function interprets the command, it must return true.
109 |
110 | onCommand(function(command) {
111 |   var commandObj = parse(command);
112 | 
113 |   if(commandObj.command == 'cache')
114 |     log('Cache content:'+ cache);
115 | });
116 |         
117 |
onIdle(handlerFunc)
121 | Trigger the handlerFunc function when the simulator has no message to send.
123 |
124 | onIdle(function(command) {
125 |   // ...
126 | });
127 |         
128 |
131 | 132 |

A Plugin Example

133 | 134 |

First of all, the plugins need to be in the plugins/ folder which already contains some examples.

135 | 136 |

A primitive plugin template file looks like:

137 | 138 |
var myPlugin = {
139 |   name: '',
140 |   description: '',
141 |   author: '',
142 | 
143 |   ocpp_version: '',
144 |   system: '',
145 | 
146 |   onLoad: function() {
147 | 
148 |   }
149 | };
150 | 
151 | export.modules = myPlugin;
152 | 
153 | 154 |

Every plugin must have a onLoad function, this is the entry point, the function triggered right after loading the plugin. All the other fields are optional.

155 | 156 |

Within the framework of this document, we are going to build a charge point plugin which:

157 | 158 |
    159 |
  • Sends a BootNotification at start.
  • 160 |
  • Sets a result function for the BootNotification procedure.
  • 161 |
  • Calls a StartTransaction procedure when a RemoteStartTransaction is received.
  • 162 |
  • Displays a message when the connection is lost.
  • 163 |
  • Create a new "hello" command for introducing the plugin.
  • 164 |
165 | 166 |

Calling a procedure

167 | 168 |

Right after loading our plugin, this one sends a BootNotification to the Central System.

169 | 170 |
var myPlugin = {
171 |   name: 'My Plugin',
172 |   description: 'Plugin example with the tutorial',
173 |   author: 'myself',
174 | 
175 |   ocpp_version: '1.5',
176 |   system: 'cs',
177 | 
178 |   onLoad: function() {
179 |     var self = this;
180 | 
181 |     self.cp.call('BootNotification');
182 |   }
183 | };
184 | 
185 | export.modules = myPlugin;
186 | 
187 | 188 |

The API also provides a second parameter for the call function in order to customize the arguments of the message.

189 | 190 |
var myPlugin = {
191 |   name: 'My Plugin',
192 |   description: 'Charge point plugin example for the tutorial',
193 |   author: 'myself',
194 | 
195 |   ocpp_version: '1.5',
196 |   system: 'cs',
197 | 
198 |   onLoad: function() {
199 |     var self = this;
200 | 
201 |     self.cp.call('BootNotification', {
202 |       chargePointVendor: "Vendor",
203 |       chargePointModel: "Model"
204 |     });
205 |   }
206 | };
207 | 
208 | export.modules = myPlugin;
209 | 
210 | 211 |

Setting a result function

212 | 213 |

We want our plugin to display the heartbeatInterval when it receives a response from the BootNotification call. So, we need to write an onResult callback function.

214 | 215 |
var myPlugin = {
216 |   name: 'My Plugin',
217 |   description: 'Charge point plugin example for the tutorial',
218 |   author: 'myself',
219 | 
220 |   ocpp_version: '1.5',
221 |   system: 'cs',
222 | 
223 |   onLoad: function() {
224 |     var self = this;
225 | 
226 |     self.cp.call('BootNotification', {
227 |       chargePointVendor: "Vendor",
228 |       chargePointModel: "Model"
229 |     });
230 | 
231 |     self.onResult('BootNotification', function(values) {
232 |       self.log('Heartbeat interval value:' + values.heartbeatInterval);
233 |     });
234 |   }
235 | };
236 | 
237 | export.modules = myPlugin;
238 | 
239 | 240 |

Setting a call function

241 | 242 |

We want our charge point to start a new transaction when the central system calls a RemoteStartTransaction remote procedure.

243 | 244 |
var myPlugin = {
245 |   name: 'My Plugin',
246 |   description: 'Charge point plugin example for the tutorial',
247 |   author: 'myself',
248 | 
249 |   ocpp_version: '1.5',
250 |   system: 'cs',
251 | 
252 |   onLoad: function() {
253 |     var self = this;
254 | 
255 |     self.cp.call('BootNotification', {
256 |       chargePointVendor: "Vendor",
257 |       chargePointModel: "Model"
258 |     });
259 | 
260 |     self.onResult('BootNotification', function(values) {
261 |       self.log('Heartbeat interval value:' + values.heartbeatInterval);
262 |     });
263 | 
264 |     self.onCall('RemoteStartTransaction', function(values) {
265 |       self.cp.call('StartTransaction');
266 |     });
267 | 
268 |   }
269 | };
270 | 
271 | export.modules = myPlugin;
272 | 
273 | 274 |

Setting a function when a connection event occurs

275 | 276 |

When the connection between the charge point and the central system is lost, we want to display a message.

277 | 278 |
var myPlugin = {
279 |   name: 'My Plugin',
280 |   description: 'Charge point plugin example for the tutorial',
281 |   author: 'myself',
282 | 
283 |   ocpp_version: '1.5',
284 |   system: 'cs',
285 | 
286 |   onLoad: function() {
287 |     var self = this;
288 | 
289 |     self.cp.call('BootNotification', {
290 |       chargePointVendor: "Vendor",
291 |       chargePointModel: "Model"
292 |     });
293 | 
294 |     self.onResult('BootNotification', function(values) {
295 |       self.log('Heartbeat interval value:' + values.heartbeatInterval);
296 |     });
297 | 
298 |     self.onCall('RemoteStartTransaction', function(values) {
299 |       self.cp.call('StartTransaction');
300 |     });
301 | 
302 |     self.onConnectEvent(function(type, cbId) {
303 |       if(type == 'disconnected')
304 |         self.log('Connection to the Central System lost !');
305 |     });
306 | 
307 |   }
308 | };
309 | 
310 | export.modules = myPlugin;
311 | 
312 | 313 |

Creating a custom command

314 | 315 |

The API provides functions for parsing what the user enters in the command line interface. This process also allows the developer to create new commands. In our case, we want to create a "hello" command for displaying informations about the plugin.

316 | 317 |
var myPlugin = {
318 |   name: 'My Plugin',
319 |   description: 'Charge point plugin example for the tutorial',
320 |   author: 'myself',
321 | 
322 |   ocpp_version: '1.5',
323 |   system: 'cs',
324 | 
325 |   onLoad: function() {
326 |     var self = this;
327 | 
328 |     self.cp.call('BootNotification', {
329 |       chargePointVendor: "Vendor",
330 |       chargePointModel: "Model"
331 |     });
332 | 
333 |     self.onResult('BootNotification', function(values) {
334 |       self.log('Heartbeat interval value:' + values.heartbeatInterval);
335 |     });
336 | 
337 |     self.onCall('RemoteStartTransaction', function(values) {
338 |       self.cp.call('StartTransaction');
339 |     });
340 | 
341 |     self.onConnectEvent(function(type, cbId) {
342 |       if(type == 'disconnected')
343 |         self.log('Connection to the Central System lost !');
344 |     });
345 | 
346 |     self.onCommand(function(command) {
347 |       var commandObj = self.parse(command);
348 | 
349 |       if(commandObj.command == 'hello') {
350 |         self.log("Hi! I'm "+ myPlugin.name +", created by "+ myPlugin.author +"!");
351 |         return true;
352 |       }
353 |     });
354 | 
355 | 
356 |   }
357 | };
358 | 
359 | export.modules = myPlugin;
360 | 
361 | 362 |

Important: If you want the simulator not to parse the command afterwards, you must return true.

363 | 364 |

Running the plugin

365 | 366 |

For testing the plugin, at first, run a charge point simulator. Available plugins can be viewed using the 'plugins' command:

367 | 368 |
> plugins
369 | List of plugins:
370 | [ ] cbxs-automatic-mode.js
371 | [ ] cs-example.js
372 | [ ] helloworld.js
373 | [ ] myplugin.js
374 | 
375 | 376 |

For loading our plugin, just type:

377 | 378 |
> load myplugin
379 | 
380 | > plugins
381 | List of plugins:
382 | [ ] cbxs-automatic-mode.js
383 | [ ] cs-example.js
384 | [ ] helloworld.js
385 | [X] myplugin.js
386 | 
387 | 388 |

If you want to stop the plugin without stopping the program, just type:

389 | 390 |
> unload myplugin
391 | 
392 | -------------------------------------------------------------------------------- /rfid/MFRC522.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf8 -*- 3 | 4 | import RPi.GPIO as GPIO 5 | import spi 6 | import signal 7 | import time 8 | 9 | class MFRC522: 10 | NRSTPD = 22 11 | 12 | MAX_LEN = 16 13 | 14 | PCD_IDLE = 0x00 15 | PCD_AUTHENT = 0x0E 16 | PCD_RECEIVE = 0x08 17 | PCD_TRANSMIT = 0x04 18 | PCD_TRANSCEIVE = 0x0C 19 | PCD_RESETPHASE = 0x0F 20 | PCD_CALCCRC = 0x03 21 | 22 | PICC_REQIDL = 0x26 23 | PICC_REQALL = 0x52 24 | PICC_ANTICOLL = 0x93 25 | PICC_SElECTTAG = 0x93 26 | PICC_AUTHENT1A = 0x60 27 | PICC_AUTHENT1B = 0x61 28 | PICC_READ = 0x30 29 | PICC_WRITE = 0xA0 30 | PICC_DECREMENT = 0xC0 31 | PICC_INCREMENT = 0xC1 32 | PICC_RESTORE = 0xC2 33 | PICC_TRANSFER = 0xB0 34 | PICC_HALT = 0x50 35 | 36 | MI_OK = 0 37 | MI_NOTAGERR = 1 38 | MI_ERR = 2 39 | 40 | Reserved00 = 0x00 41 | CommandReg = 0x01 42 | CommIEnReg = 0x02 43 | DivlEnReg = 0x03 44 | CommIrqReg = 0x04 45 | DivIrqReg = 0x05 46 | ErrorReg = 0x06 47 | Status1Reg = 0x07 48 | Status2Reg = 0x08 49 | FIFODataReg = 0x09 50 | FIFOLevelReg = 0x0A 51 | WaterLevelReg = 0x0B 52 | ControlReg = 0x0C 53 | BitFramingReg = 0x0D 54 | CollReg = 0x0E 55 | Reserved01 = 0x0F 56 | 57 | Reserved10 = 0x10 58 | ModeReg = 0x11 59 | TxModeReg = 0x12 60 | RxModeReg = 0x13 61 | TxControlReg = 0x14 62 | TxAutoReg = 0x15 63 | TxSelReg = 0x16 64 | RxSelReg = 0x17 65 | RxThresholdReg = 0x18 66 | DemodReg = 0x19 67 | Reserved11 = 0x1A 68 | Reserved12 = 0x1B 69 | MifareReg = 0x1C 70 | Reserved13 = 0x1D 71 | Reserved14 = 0x1E 72 | SerialSpeedReg = 0x1F 73 | 74 | Reserved20 = 0x20 75 | CRCResultRegM = 0x21 76 | CRCResultRegL = 0x22 77 | Reserved21 = 0x23 78 | ModWidthReg = 0x24 79 | Reserved22 = 0x25 80 | RFCfgReg = 0x26 81 | GsNReg = 0x27 82 | CWGsPReg = 0x28 83 | ModGsPReg = 0x29 84 | TModeReg = 0x2A 85 | TPrescalerReg = 0x2B 86 | TReloadRegH = 0x2C 87 | TReloadRegL = 0x2D 88 | TCounterValueRegH = 0x2E 89 | TCounterValueRegL = 0x2F 90 | 91 | Reserved30 = 0x30 92 | TestSel1Reg = 0x31 93 | TestSel2Reg = 0x32 94 | TestPinEnReg = 0x33 95 | TestPinValueReg = 0x34 96 | TestBusReg = 0x35 97 | AutoTestReg = 0x36 98 | VersionReg = 0x37 99 | AnalogTestReg = 0x38 100 | TestDAC1Reg = 0x39 101 | TestDAC2Reg = 0x3A 102 | TestADCReg = 0x3B 103 | Reserved31 = 0x3C 104 | Reserved32 = 0x3D 105 | Reserved33 = 0x3E 106 | Reserved34 = 0x3F 107 | 108 | serNum = [] 109 | 110 | def __init__(self, dev='/dev/spidev0.0', spd=1000000): 111 | spi.openSPI(device=dev,speed=spd) 112 | GPIO.setmode(GPIO.BOARD) 113 | GPIO.setup(22, GPIO.OUT) 114 | GPIO.output(self.NRSTPD, 1) 115 | self.MFRC522_Init() 116 | 117 | def MFRC522_Reset(self): 118 | self.Write_MFRC522(self.CommandReg, self.PCD_RESETPHASE) 119 | 120 | def Write_MFRC522(self, addr, val): 121 | spi.transfer(((addr<<1)&0x7E,val)) 122 | 123 | def Read_MFRC522(self, addr): 124 | val = spi.transfer((((addr<<1)&0x7E) | 0x80,0)) 125 | return val[1] 126 | 127 | def SetBitMask(self, reg, mask): 128 | tmp = self.Read_MFRC522(reg) 129 | self.Write_MFRC522(reg, tmp | mask) 130 | 131 | def ClearBitMask(self, reg, mask): 132 | tmp = self.Read_MFRC522(reg); 133 | self.Write_MFRC522(reg, tmp & (~mask)) 134 | 135 | def AntennaOn(self): 136 | temp = self.Read_MFRC522(self.TxControlReg) 137 | if(~(temp & 0x03)): 138 | self.SetBitMask(self.TxControlReg, 0x03) 139 | 140 | def AntennaOff(self): 141 | self.ClearBitMask(self.TxControlReg, 0x03) 142 | 143 | def MFRC522_ToCard(self,command,sendData): 144 | backData = [] 145 | backLen = 0 146 | status = self.MI_ERR 147 | irqEn = 0x00 148 | waitIRq = 0x00 149 | lastBits = None 150 | n = 0 151 | i = 0 152 | 153 | if command == self.PCD_AUTHENT: 154 | irqEn = 0x12 155 | waitIRq = 0x10 156 | if command == self.PCD_TRANSCEIVE: 157 | irqEn = 0x77 158 | waitIRq = 0x30 159 | 160 | self.Write_MFRC522(self.CommIEnReg, irqEn|0x80) 161 | self.ClearBitMask(self.CommIrqReg, 0x80) 162 | self.SetBitMask(self.FIFOLevelReg, 0x80) 163 | 164 | self.Write_MFRC522(self.CommandReg, self.PCD_IDLE); 165 | 166 | while(i self.MAX_LEN: 202 | n = self.MAX_LEN 203 | 204 | i = 0 205 | while i) is a communication 8 | protocol between multiple charging stations ("charge points") and a single 9 | management software ("central system"). 10 | 11 | Currently (december 2012), two OCPP versions (1.2 and 1.5) have been released. 12 | There is a draft in progress for a new version (2.0). 13 | Both existing versions use SOAP over HTTP as the RPC/transport protocol: 14 | 15 | +---------------+ soap/http client soap/http server +---------------+ 16 | | |--------------------------------------->| | 17 | | | Operations initiated by ChargePoint | | 18 | | ChargePoint | | CentralSystem | 19 | | | soap/http server soap/http client | | 20 | | |<---------------------------------------| | 21 | +---------------+ Operations initiated by CentralSystem +---------------+ 22 | 23 | The two main problems with this solution are: 24 | 25 | - It requires the CentralSystem to be able to establish a TCP/IP connection 26 | to the ChargePoint. This is often not possible on IP based mobile data 27 | networks using a public APN, where the ChargePoint is assigned a private IP 28 | address on the operator network. In such cases, the ChargePoint can access 29 | the Internet (using NAT), but no connection can be made from the Internet to 30 | the ChargePoint. A possible solution is to set up a VPN and/or a private APN, 31 | but the cost is generally prohibitive for small installations. 32 | - SOAP is very verbose, with a large overhead on every message. This causes 33 | a non-optimal bandwidth consumption, which can raise the running cost of 34 | an installation. 35 | 36 | This project proposes a new RPC/transport for OCPP, that addresses 37 | these two issues, using Websocket () for the 38 | transport layer, and SPRC (see Protocol Specification section below) for the 39 | RPC layer. 40 | 41 | 42 | ## Architecture 43 | 44 | This schema shows objects instances for a system with 45 | one ChargePointSimulator and one CentralSystemSimulator. 46 | 47 | +------------------------+ +------------------------+ 48 | | ChargePointSimulator | | CentralSystemSimulator | 49 | +------------------------+ +------------------------+ 50 | |create (param: url) |create (param: listening tcp port) 51 | V V 52 | +------------------------+ connect +------------------------+ 53 | | WebSocketClient |------------>| WebSocketServer | 54 | +------------------------+ +------------------------+ 55 | 56 | When a WebSocket connection (i.e. a transport layer) is established, 57 | each peer creates: 58 | 59 | - a WampClientConnection, to make RPC calls 60 | - a WampServerConnection, to handle RPC calls 61 | 62 | This gives two independant RPC channels on a single TCP connection: 63 | 64 | +------------------------+ +------------------------+ 65 | +-| ChargePointSimulator | | CentralSystemSimulator |-+ 66 | | +------------------------+ +------------------------+ | 67 | | create (param: connection) create (param: connection)| 68 | | | 69 | | +------------------------+ +------------------------+ | 70 | +>| WampClientConnection |------------>| WampServerConnection |<+ 71 | | +------------------------+ RPC call +------------------------+ | 72 | | (e.g. MeterValue) | 73 | | | 74 | | +------------------------+ +------------------------+ | 75 | +>| WampServerConnection |<------------| WampClientConnection |<+ 76 | +------------------------+ RPC call +------------------------+ 77 | (e.g. Reset) 78 | 79 | ## Installation & usage 80 | 81 | This program requires Node.js (). 82 | Third-party packages can be installed with the npm utility. 83 | Currently, ocppjs depends on 'websocket', 'xml2js', 'node-expat', 84 | 'request' and 'jayschema' packages: 85 | 86 | % npm install 87 | 88 | To run the OCPP.js simulator, just enter 89 | 90 | % node gir-ocppjs.js 91 | 92 | You will be given a prompt where you can enter commands. 93 | 94 | Commands syntax: 95 | 96 | - `start_cs [options]`: Start a CentralSystem 97 | simulator, listening on the specified TCP ``. 98 | This command can be directly specified on the command line. 99 | Options: 100 | `-i `: websocket ping interval (default: 300s) 101 | `-u `: endpoint URL (default: '/') 102 | `-p `: websocket protocol name (default: 'ocpp1.2,ocpp1.5') 103 | - `start_cp `: Start a ChargePoint simulator, which will try to 104 | connect to a CentralSystem on the specified URL, using the specified 105 | chargepoint ``. 106 | This command can be directly specified on the command line. 107 | Options: 108 | `-p `: websocket protocol name (default: 'ocpp1.5') 109 | `-i `: websocket ping interval (default: 300s) 110 | `-t `: transport layer mode (websocket or soap), default: websocket 111 | `-f `: in SOAP mode, defines the SOAP From header. 112 | `-r `: in SOAP mode, defines the remote action port. 113 | - `remote_ [options] [custom_arguments]`: When a CentralSystem simulator is 114 | running and has a connected chargepoint, perform a `` 115 | remote action. 116 | See `help` for available commands. 117 | Options: 118 | `--remote-id `: send the remote action to the specified 119 | chargepoint ``. This parameter can be omitted when a single chargepoint 120 | is connected. 121 | Custom Arguments: see section `Argument customization` below. 122 | - ` [options] [custom_arguments]`: When a ChargePoint simulator `` is 123 | running and is connected to a CentralSystem, send a `` message. 124 | See `help` for available commands. 125 | Options: 126 | `--id id`: send the message using the specified chargepoint ``. 127 | This parameter can be omitted when a single chargepoint simulator is running. 128 | Custom Arguments: see section `Argument customization` below. 129 | - `set [arguments]`: modify the simulator settings from command line 130 | interface. 131 | Arguments: setting=value ... Supported settings: `websocket_ping_interval`. 132 | 133 | > set cs websocket_ping_interval=120 134 | 135 | - `help`: List available commands. 136 | - `quit`: Exit the program. 137 | 138 | 139 | Argument customization: 140 | 141 | When typing `remote_` or ``, the fields of the payload can be 142 | customized individually from the command line interface using the following 143 | syntax: 144 | 145 | argument=value ... 146 | 147 | > bootnotification chargePointVendor="DBT" 148 | > bootnotification chargePointVendor="DBT" chargePointModel="NQC-ACDC" 149 | 150 | `value` handles `string`, `number`, `object` and `array` types. 151 | 152 | 153 | ### Example - Running a CentralSytem 154 | 155 | % node gir-ocppjs.js start_cs 9000 156 | [2012-12-28 14:54:32] CentralSystem started. 157 | [2012-12-28 14:54:32] cs: Server is listening on port 9000 158 | [2012-12-28 14:54:44] cs: ChargePoint #0 connected from 127.0.0.1. 159 | 160 | > 161 | [2012-12-28 14:54:54] cs: <>cp#0 [3,"V7NeDL1MnbkaQcaHcYnvxOpCdcI4gpAV", 165 | {"status":"Accepted", 166 | "currentTime":"2012-11-28T13:54:44Z", 167 | "heartbeatInterval":300}] 168 | 169 | > remote_reset type="Soft" 170 | [2012-12-28 14:56:50] cs: >>cp#0 [2,"tU8hVs8yY6Aleie1kN8fXD7YUJ0PDuyc", 171 | "Reset",{"type":"Soft"}] 172 | [2012-12-28 14:56:50] cs: < 176 | [2012-12-28 14:58:22] cs: <>cp#0 [3,"Shfrho1gR2NmsjCVeHpCOjImdNxhPPXq", 180 | {}] 181 | 182 | > 183 | [2012-12-28 14:59:44] cs: <>cp#0 [3,"yUtxCAuRRv26dKRuQ1fy1AYvE0fZTZ2Z", 186 | {"currentTime":"2012-12-28T13:59:44Z"}] 187 | 188 | ### Example - Running a ChargePoint 189 | 190 | % node gir-ocppjs.js start_cp ws://localhost:9000 0 191 | [2012-12-28 14:54:44] ChargePoint #0 started. 192 | [2012-12-28 14:54:44] cp#0: Connected to CentralSystem. 193 | 194 | > bootnotification chargePointVendor="DBT" chargePointModel="NQC-ACDC" 195 | [2012-12-28 14:54:54] cp#0: >>cs [2,"V7NeDL1MnbkaQcaHcYnvxOpCdcI4gpAV", 196 | "BootNotification",{"chargePointVendor":"DBT", 197 | "chargePointModel":"NQC-ACDC",...}] 198 | [2012-12-28 14:54:54] cp#0: < 204 | [2012-12-28 14:56:50] cp#0: <>cs [3,"tU8hVs8yY6Aleie1kN8fXD7YUJ0PDuyc", 207 | {"status":"Accepted"}] 208 | 209 | > metervalues 210 | [2012-12-28 14:58:22] cp#0: >>cs [2,"Shfrho1gR2NmsjCVeHpCOjImdNxhPPXq", 211 | "MeterValues", {"connectorId":2, 212 | values:[{"timestamp":"2012-12-28T13:58:22Z", 213 | "value":0}]}] 214 | [2012-12-28 14:58:22] cp#0: < heartbeat 218 | [2012-12-28 14:59:44] cp#0: >>cs [2,"yUtxCAuRRv26dKRuQ1fy1AYvE0fZTZ2Z", 219 | "Heartbeat",{}] 220 | [2012-12-28 14:59:44] cp#0: <>cp#boxid /BootNotification {"status":"Accepted", 233 | "currentTime":"2013-02-01T15:09:18.000Z", 234 | "heartbeatInterval":1200} 235 | 236 | > remote_starttransaction 237 | [2013-04-22 17:01:48] cs: >>cp#boxid /RemoteStartTransaction {"idTag": 238 | "044943121F1D80","connectorId":2} 239 | [2013-04-22 17:01:48] cs: < bootnotification 249 | [2013-04-09 14:57:29] cp#boxid: >>cs /BootNotification {"chargePointVendor": 250 | "DBT", "chargePointModel":"NQC-ACDC", ...} 251 | [2013-04-09 14:57:29] cs: >>boxid /BootNotification {"status":"Accepted", 252 | "currentTime":"2013-02-01T15:09:18.000Z", 253 | "heartbeatInterval":1200} 254 | ... 255 | 256 | [2013-04-22 17:01:48] cs: <>cp#boxid /RemoteStartTransaction {"status": 259 | "Accepted"} 260 | 261 | > set cp print_xml=true 262 | 263 | > heartbeat 264 | [2013-04-09 15:03:02] cp#boxid: >>cs 265 | ... 266 | 267 | 268 | [2013-04-09 15:03:02] cs: >>boxid 269 | 270 | 2013-02-01T15:09:18Z 271 | 272 | 273 | ## Unit testing 274 | 275 | ocppjs provides a framework and four example files (in the test/ folder) for 276 | writing unit tests. If a test fails, the errors are printed on the standard 277 | output. If it succeeds then nothing is displayed. 278 | 279 | ### Example - Running a test 280 | 281 | % cd test/ 282 | % node test-cp-1.2.js 283 | % 284 | 285 | ## Plugins 286 | 287 | ocppjs provides a plugin system which allows developers to define the simulator 288 | behavior without modifying the program source code. Plugins consist of extern 289 | JavaScript files listed in the *plugins/* folder. 290 | 291 | From the prompt of the simulator, 3 commands are available: 292 | 293 | ## Documentation 294 | 295 | The gir-ocppjs-doc.js program generates JSON examples from WSDL files. 296 | Run it with: 297 | 298 | % npm install xml2js 299 | % cd doc 300 | % make 301 | 302 | Output files generated: 303 | 304 | * 305 | * 306 | 307 | ### Protocol specification 308 | 309 | A draft specification for a WebSocket-based OCPP transport is available: 310 | 311 | 312 | ## Download 313 | 314 | 315 | -------------------------------------------------------------------------------- /ocpp/doc/wsdl/ocpp_chargepointservice_1.2_final.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 9 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | Defines the unlock-status-value 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | Defines the UnlockConnector.req PDU 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | Defines the UnlockConnector.conf PDU 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | Defines the reset-type-value 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | Defines the Reset.req PDU 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | Defines the reset-status-value 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | Defines the availability-type-value 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | Defines the ChangeAvailability.req PDU 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | Defines the availability-status-value 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | Defines the ChangeAvailability.conf PDU 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | Defines the GetDiagnostics.req PDU 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | Defines the GetDiagnostics.conf PDU 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | Defines the ClearCache.req PDU 162 | 163 | 164 | 165 | 166 | 167 | Defines the clear-cache-status-value 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | Defines the ClearCache.conf PDU 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | Defines the ChangeConfiguration.req PDU 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | Defines the configuration-status-value 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | Defines the ChangeConfiguration.conf PDU 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | Defines the RemoteStartTransaction.req PDU 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | Defines the remote-start-stop-status-value 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | Defines the RemoteStartTransaction.conf PDU 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | Defines the RemoteStopTransaction.req PDU 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | Defines the RemoteStopTransaction.conf PDU 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | The ChargePoint Service for the Open Charge Point Protocol 529 | 530 | 531 | 532 | 533 | -------------------------------------------------------------------------------- /ocpp/doc/wsdl/ocpp_centralsystemservice_1.2_final.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 9 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | Defines the authorization-status-value 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | Defines the Authorize.req PDU 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | Defines the Authorize.conf PDU 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Defines the StartTransaction.req PDU 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | Defines the StartTransaction.conf PDU 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | Defines the StopTransaction.req PDU 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | Defines the StopTransaction.conf PDU 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | Defines the Heartbeat.req PDU 119 | 120 | 121 | 122 | 123 | 124 | Defines the Heartbeat.conf PDU 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | Defines single value of the meter-value-value 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | Defines the MeterValues.req PDU 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | Defines the MeterValues.conf PDU 154 | 155 | 156 | 157 | 158 | 159 | Defines the BootNotification.req PDU 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | Defines the registration-status-value 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | Defines the BootNotification.conf PDU 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | Defines the charge-point-error-value 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | Defines the charge-point-status-value 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | Defines the StatusNotification.req PDU 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | Defines the StatusNotification.conf PDU 237 | 238 | 239 | 240 | 241 | 242 | Defines the firmware-status-value 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | Defines the FirmwareStatusNotification.req PDU 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | Defines the FirmwareStatusNotification.conf PDU 264 | 265 | 266 | 267 | 268 | 269 | Defines the diagnostics-status-value 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | Defines the DiagnosticsStatusNotification.req PDU 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | Defines the DiagnosticsStatusNotification.conf PDU 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | The Central System Service for the Open Charge Point Protocol 547 | 548 | 549 | 550 | 551 | --------------------------------------------------------------------------------