ycache__/urls.cpython-39.pyc">Wyświetl plik

BIN
app/front/__pycache__/views.cpython-39.pyc


BIN
app/front/migrations/__pycache__/__init__.cpython-39.pyc


+ 132 - 0
app/front/static/front/css/blog.css

@@ -0,0 +1,132 @@
1
+/* stylelint-disable selector-list-comma-newline-after */
2
+
3
+.blog-header {
4
+  line-height: 1;
5
+  border-bottom: 1px solid #e5e5e5;
6
+}
7
+
8
+.blog-header-logo {
9
+  font-family: "Playfair Display", Georgia, "Times New Roman", serif;
10
+  font-size: 2.25rem;
11
+}
12
+
13
+.blog-header-logo:hover {
14
+  text-decoration: none;
15
+}
16
+
17
+h1, h2, h3, h4, h5, h6 {
18
+  font-family: "Playfair Display", Georgia, "Times New Roman", serif;
19
+}
20
+
21
+.display-4 {
22
+  font-size: 2.5rem;
23
+}
24
+@media (min-width: 768px) {
25
+  .display-4 {
26
+    font-size: 3rem;
27
+  }
28
+}
29
+
30
+.nav-scroller {
31
+  position: relative;
32
+  z-index: 2;
33
+  height: 2.75rem;
34
+  overflow-y: hidden;
35
+}
36
+
37
+.nav-scroller .nav {
38
+  display: -webkit-box;
39
+  display: -ms-flexbox;
40
+  display: flex;
41
+  -ms-flex-wrap: nowrap;
42
+  flex-wrap: nowrap;
43
+  padding-bottom: 1rem;
44
+  margin-top: -1px;
45
+  overflow-x: auto;
46
+  text-align: center;
47
+  white-space: nowrap;
48
+  -webkit-overflow-scrolling: touch;
49
+}
50
+
51
+.nav-scroller .nav-link {
52
+  padding-top: .75rem;
53
+  padding-bottom: .75rem;
54
+  font-size: .875rem;
55
+}
56
+
57
+.card-img-right {
58
+  height: 100%;
59
+  border-radius: 0 3px 3px 0;
60
+}
61
+
62
+.flex-auto {
63
+  -ms-flex: 0 0 auto;
64
+  -webkit-box-flex: 0;
65
+  flex: 0 0 auto;
66
+}
67
+
68
+.h-250 { height: 250px; }
69
+@media (min-width: 768px) {
70
+  .h-md-250 { height: 250px; }
71
+}
72
+
73
+.border-top { border-top: 1px solid #e5e5e5; }
74
+.border-bottom { border-bottom: 1px solid #e5e5e5; }
75
+
76
+.box-shadow { box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); }
77
+
78
+/*
79
+ * Blog name and description
80
+ */
81
+.blog-title {
82
+  margin-bottom: 0;
83
+  font-size: 2rem;
84
+  font-weight: 400;
85
+}
86
+.blog-description {
87
+  font-size: 1.1rem;
88
+  color: #999;
89
+}
90
+
91
+@media (min-width: 40em) {
92
+  .blog-title {
93
+    font-size: 3.5rem;
94
+  }
95
+}
96
+
97
+/* Pagination */
98
+.blog-pagination {
99
+  margin-bottom: 4rem;
100
+}
101
+.blog-pagination > .btn {
102
+  border-radius: 2rem;
103
+}
104
+
105
+/*
106
+ * Blog posts
107
+ */
108
+.blog-post {
109
+  margin-bottom: 4rem;
110
+}
111
+.blog-post-title {
112
+  margin-bottom: .25rem;
113
+  font-size: 2.5rem;
114
+}
115
+.blog-post-meta {
116
+  margin-bottom: 1.25rem;
117
+  color: #999;
118
+}
119
+
120
+/*
121
+ * Footer
122
+ */
123
+.blog-footer {
124
+  padding: 2.5rem 0;
125
+  color: #999;
126
+  text-align: center;
127
+  background-color: #f9f9f9;
128
+  border-top: .05rem solid #e5e5e5;
129
+}
130
+.blog-footer p:last-child {
131
+  margin-bottom: 0;
132
+}

+ 34 - 0
app/front/templates/front/_menu.html

@@ -0,0 +1,34 @@
1
+<header class="blog-header py-3">
2
+	<div class="row flex-nowrap justify-content-between align-items-center">
3
+		<div class="col-4 pt-1">
4
+			<a class="text-muted" href="#">Subscribe</a>
5
+		</div>
6
+		<div class="col-4 text-center">
7
+			<a class="blog-header-logo text-dark" href="{% url "front:index" %}">SimpleMarket</a>
8
+		</div>
9
+		<div class="col-4 d-flex justify-content-end align-items-center">
10
+			<a class="text-muted" href="#">
11
+				<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mx-3"><circle cx="10.5" cy="10.5" r="7.5"></circle><line x1="21" y1="21" x2="15.8" y2="15.8"></line></svg>
12
+			</a>
13
+			<a class="btn btn-sm btn-outline-secondary" href="#">Sign up</a>
14
+		</div>
15
+	</div>
16
+</header>
17
+
18
+<div class="nav-scroller py-1 mb-2">
19
+	<nav class="nav d-flex justify-content-between">
20
+		<a class="p-2 text-muted" href="#">World</a>
21
+		<a class="p-2 text-muted" href="#">U.S.</a>
22
+		<a class="p-2 text-muted" href="#">Technology</a>
23
+		<a class="p-2 text-muted" href="#">Design</a>
24
+		<a class="p-2 text-muted" href="#">Culture</a>
25
+		<a class="p-2 text-muted" href="#">Business</a>
26
+		<a class="p-2 text-muted" href="#">Politics</a>
27
+		<a class="p-2 text-muted" href="#">Opinion</a>
28
+		<a class="p-2 text-muted" href="#">Science</a>
29
+		<a class="p-2 text-muted" href="#">Health</a>
30
+		<a class="p-2 text-muted" href="#">Style</a>
31
+		<a class="p-2 text-muted" href="#">Travel</a>
32
+	</nav>
33
+</div>
34
+

+ 56 - 0
app/front/templates/front/base.html

@@ -0,0 +1,56 @@
1
+{% load static %}
2
+<!DOCTYPE html>
3
+<html lang="en">
4
+<head>
5
+    <title>
6
+        FindBed.xyz ทำเพื่อช่วยเหลือในการหาเตียงสำหรับผู้ป่วย COVID19
7
+    </title>
8
+    <meta name="description" content="FindBed.xyz ทำเพื่อช่วยเหลือในการหาเตียงสำหรับผู้ป่วย COVID19">
9
+     <!-- Include meta tag to ensure proper rendering and touch zooming -->
10
+  <meta name="viewport" content="width=device-width, initial-scale=1">
11
+  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
12
+
13
+  <!-- Include the jQuery library -->
14
+  <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
15
+<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
16
+<script>
17
+var _hmt = _hmt || [];
18
+(function() {
19
+var hm = document.createElement("script");
20
+hm.src = "//hm.baidu.com/hm.js?73c27e26f610eb3c9f3feb0c75b03925";
21
+var s = document.getElementsByTagName("script")[0];
22
+s.parentNode.insertBefore(hm, s);
23
+})();
24
+</script>
25
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css">
26
+
27
+  <link href="https://fonts.googleapis.com/css?family=Playfair+Display:700,900" rel="stylesheet">
28
+    <link href="{% static "front/css/blog.css" %}" rel="stylesheet">
29
+<script type='text/javascript' src="{% static "js/main.js" %}"></script>
30
+</head>
31
+
32
+<body>
33
+	<div class="container">
34
+		{% include "front/_menu.html" %}
35
+		{% block top_slide %}	
36
+		{% endblock %}  
37
+	</div>
38
+	<main role="main" class="container">
39
+		{% block content %}
40
+		{% endblock %}
41
+	</main><!-- /.container -->
42
+	<footer class="blog-footer">
43
+		<p>Blog template built for <a href="https://getbootstrap.com/">Bootstrap</a> by <a href="https://twitter.com/mdo">@mdo</a>.</p>
44
+		<p>
45
+		<a href="#">Back to top</a>
46
+		</p>
47
+	</footer>
48
+
49
+
50
+    {% block foot_script %}
51
+    {% endblock %}
52
+<script src="{% static "bower_components/holderjs/holder.min.js" %}"></script>
53
+<!-- 
54
+<script src="{% static "bower_components/popper.js/src/popper.js" %}"></script> -->
55
+</body>
56
+</html>

+ 150 - 35
app/front/templates/front/index.html

@@ -1,39 +1,154 @@
1
-{% extends "base.html" %}
1
+{% extends "front/base.html" %}
2 2
 
3 3
 {% load static %}
4
+{% block top_slide %}
5
+
6
+		<div class="jumbotron p-3 p-md-5 text-white rounded bg-dark">
7
+			<div class="col-md-6 px-0">
8
+				<h1 class="display-4 font-italic">Title of a longer featured blog post</h1>
9
+				<p class="lead my-3">Multiple lines of text that form the lede, informing new readers quickly and efficiently about what's most interesting in this post's contents.</p>
10
+				<p class="lead mb-0"><a href="#" class="text-white font-weight-bold">Continue reading...</a></p>
11
+			</div>
12
+		</div>
13
+
14
+		<div class="row mb-2">
15
+			<div class="col-md-6">
16
+				<div class="card flex-md-row mb-4 box-shadow h-md-250">
17
+					<div class="card-body d-flex flex-column align-items-start">
18
+						<strong class="d-inline-block mb-2 text-primary">World</strong>
19
+						<h3 class="mb-0">
20
+							<a class="text-dark" href="#">Featured post</a>
21
+						</h3>
22
+						<div class="mb-1 text-muted">Nov 12</div>
23
+						<p class="card-text mb-auto">This is a wider card with supporting text below as a natural lead-in to additional content.</p>
24
+						<a href="#">Continue reading</a>
25
+					</div>
26
+					<img class="card-img-right flex-auto d-none d-md-block" data-src="holder.js/200x250?theme=thumb" alt="Card image cap">
27
+				</div>
28
+			</div>
29
+			<div class="col-md-6">
30
+				<div class="card flex-md-row mb-4 box-shadow h-md-250">
31
+					<div class="card-body d-flex flex-column align-items-start">
32
+						<strong class="d-inline-block mb-2 text-success">Design</strong>
33
+						<h3 class="mb-0">
34
+							<a class="text-dark" href="#">Post title</a>
35
+						</h3>
36
+						<div class="mb-1 text-muted">Nov 11</div>
37
+						<p class="card-text mb-auto">This is a wider card with supporting text below as a natural lead-in to additional content.</p>
38
+						<a href="#">Continue reading</a>
39
+					</div>
40
+					<img class="card-img-right flex-auto d-none d-md-block" data-src="holder.js/200x250?theme=thumb" alt="Card image cap">
41
+				</div>
42
+			</div>
43
+		</div>
44
+{% endblock %}
4 45
 {% block content %}
5
-<br>
6
-<div class='row'>
7
-    <form method='post' enctype="multipart/form-data">
8
-         {% csrf_token %}
9
-    <div class='col-md-12'>
10
-        <input type='text' name='firstName' class='form-control' placeholder='ชื่อ' required/>
11
-        <br>
12
-        <input type='text' name='lastName' class='form-control' placeholder='นามสกุล' required/>
13
-        <br>
14
-        <!-- 
15
-        <input type='text' name='idCard' class='form-control' placeholder='หมายเลขบัตรประชาชน' required /> 
16
-        <br> -->
17
-        <label>วันเกิด</label>
18
-        <input type='date' name='bd' class='form-control' placeholder='วันเกิด' required />
19
-        <br>
20
-        <textarea name='address' class='form-control' placeholder='ที่อยู่' required></textarea>  
21
-        <br>
22
-        <textarea name='comment' class='form-control' placeholder='ข้อความฝากถึงเจ้าหน้าที่'></textarea>  
23
-        <br>
24
-        <input type='tel' name='tel' class='form-control' placeholder='Tel.' required/>
25
-        <br>
26
-        <input type='text' name='line_id' class='form-control' placeholder='Line ID'/>
27
-        <br>
28
-        <label>อัพโหลดภาพ</label>
29
-        <input type="file" name='photo' accept="image/*;capture=camera" class='form-control'> </br>
30
-        <span class="glyphicon glyphicon-map-marker"></span>
31
-        <a class='btn btn-primary form-control' id="currentLocationBtn">
32
-        <i class="bi bi-geo-alt-fill"></i>
33
-            คลิกเพื่อแสดงตำแหน่ง</a><br><br>
34
-        <input type='text' class='form-control' name='geo' id='geoText' readonly/><br>
35
-        <input type='submit' value='ส่งข้อมูล' class='btn-success form-control'/><br><br>
36
-    </div>
37
-    </form>
38
-</div>
46
+
47
+      <div class="row">
48
+        <div class="col-md-8 blog-main">
49
+          <h3 class="pb-3 mb-4 font-italic border-bottom">
50
+            From the Firehose
51
+          </h3>
52
+
53
+          <div class="blog-post">
54
+            <h2 class="blog-post-title">Sample blog post</h2>
55
+            <p class="blog-post-meta">January 1, 2014 by <a href="#">Mark</a></p>
56
+
57
+            <p>This blog post shows a few different types of content that's supported and styled with Bootstrap. Basic typography, images, and code are all supported.</p>
58
+            <hr>
59
+            <p>Cum sociis natoque penatibus et magnis <a href="#">dis parturient montes</a>, nascetur ridiculus mus. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Sed posuere consectetur est at lobortis. Cras mattis consectetur purus sit amet fermentum.</p>
60
+            <blockquote>
61
+              <p>Curabitur blandit tempus porttitor. <strong>Nullam quis risus eget urna mollis</strong> ornare vel eu leo. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
62
+            </blockquote>
63
+            <p>Etiam porta <em>sem malesuada magna</em> mollis euismod. Cras mattis consectetur purus sit amet fermentum. Aenean lacinia bibendum nulla sed consectetur.</p>
64
+            <h2>Heading</h2>
65
+            <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
66
+            <h3>Sub-heading</h3>
67
+            <p>Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.</p>
68
+            <pre><code>Example code block</code></pre>
69
+            <p>Aenean lacinia bibendum nulla sed consectetur. Etiam porta sem malesuada magna mollis euismod. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa.</p>
70
+            <h3>Sub-heading</h3>
71
+            <p>Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean lacinia bibendum nulla sed consectetur. Etiam porta sem malesuada magna mollis euismod. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p>
72
+            <ul>
73
+              <li>Praesent commodo cursus magna, vel scelerisque nisl consectetur et.</li>
74
+              <li>Donec id elit non mi porta gravida at eget metus.</li>
75
+              <li>Nulla vitae elit libero, a pharetra augue.</li>
76
+            </ul>
77
+            <p>Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue.</p>
78
+            <ol>
79
+              <li>Vestibulum id ligula porta felis euismod semper.</li>
80
+              <li>Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.</li>
81
+              <li>Maecenas sed diam eget risus varius blandit sit amet non magna.</li>
82
+            </ol>
83
+            <p>Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis.</p>
84
+          </div><!-- /.blog-post -->
85
+
86
+          <div class="blog-post">
87
+            <h2 class="blog-post-title">Another blog post</h2>
88
+            <p class="blog-post-meta">December 23, 2013 by <a href="#">Jacob</a></p>
89
+
90
+            <p>Cum sociis natoque penatibus et magnis <a href="#">dis parturient montes</a>, nascetur ridiculus mus. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Sed posuere consectetur est at lobortis. Cras mattis consectetur purus sit amet fermentum.</p>
91
+            <blockquote>
92
+              <p>Curabitur blandit tempus porttitor. <strong>Nullam quis risus eget urna mollis</strong> ornare vel eu leo. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
93
+            </blockquote>
94
+            <p>Etiam porta <em>sem malesuada magna</em> mollis euismod. Cras mattis consectetur purus sit amet fermentum. Aenean lacinia bibendum nulla sed consectetur.</p>
95
+            <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
96
+          </div><!-- /.blog-post -->
97
+
98
+          <div class="blog-post">
99
+            <h2 class="blog-post-title">New feature</h2>
100
+            <p class="blog-post-meta">December 14, 2013 by <a href="#">Chris</a></p>
101
+
102
+            <p>Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean lacinia bibendum nulla sed consectetur. Etiam porta sem malesuada magna mollis euismod. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p>
103
+            <ul>
104
+              <li>Praesent commodo cursus magna, vel scelerisque nisl consectetur et.</li>
105
+              <li>Donec id elit non mi porta gravida at eget metus.</li>
106
+              <li>Nulla vitae elit libero, a pharetra augue.</li>
107
+            </ul>
108
+            <p>Etiam porta <em>sem malesuada magna</em> mollis euismod. Cras mattis consectetur purus sit amet fermentum. Aenean lacinia bibendum nulla sed consectetur.</p>
109
+            <p>Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue.</p>
110
+          </div><!-- /.blog-post -->
111
+
112
+          <nav class="blog-pagination">
113
+            <a class="btn btn-outline-primary" href="#">Older</a>
114
+            <a class="btn btn-outline-secondary disabled" href="#">Newer</a>
115
+          </nav>
116
+
117
+        </div><!-- /.blog-main -->
118
+
119
+        <aside class="col-md-4 blog-sidebar">
120
+          <div class="p-3 mb-3 bg-light rounded">
121
+            <h4 class="font-italic">About</h4>
122
+            <p class="mb-0">Etiam porta <em>sem malesuada magna</em> mollis euismod. Cras mattis consectetur purus sit amet fermentum. Aenean lacinia bibendum nulla sed consectetur.</p>
123
+          </div>
124
+
125
+          <div class="p-3">
126
+            <h4 class="font-italic">Archives</h4>
127
+            <ol class="list-unstyled mb-0">
128
+              <li><a href="#">March 2014</a></li>
129
+              <li><a href="#">February 2014</a></li>
130
+              <li><a href="#">January 2014</a></li>
131
+              <li><a href="#">December 2013</a></li>
132
+              <li><a href="#">November 2013</a></li>
133
+              <li><a href="#">October 2013</a></li>
134
+              <li><a href="#">September 2013</a></li>
135
+              <li><a href="#">August 2013</a></li>
136
+              <li><a href="#">July 2013</a></li>
137
+              <li><a href="#">June 2013</a></li>
138
+              <li><a href="#">May 2013</a></li>
139
+              <li><a href="#">April 2013</a></li>
140
+            </ol>
141
+          </div>
142
+
143
+          <div class="p-3">
144
+            <h4 class="font-italic">Elsewhere</h4>
145
+            <ol class="list-unstyled">
146
+              <li><a href="#">GitHub</a></li>
147
+              <li><a href="#">Twitter</a></li>
148
+              <li><a href="#">Facebook</a></li>
149
+            </ol>
150
+          </div>
151
+        </aside><!-- /.blog-sidebar -->
152
+
153
+      </div><!-- /.row -->
39 154
 {% endblock %}

+ 39 - 0
app/front/templates/front/index.old.html

@@ -0,0 +1,39 @@
1
+{% extends "front/base.html" %}
2
+
3
+{% load static %}
4
+{% block content %}
5
+<br>
6
+<div class='row'>
7
+    <form method='post' enctype="multipart/form-data">
8
+         {% csrf_token %}
9
+    <div class='col-md-12'>
10
+        <input type='text' name='firstName' class='form-control' placeholder='ชื่อ' required/>
11
+        <br>
12
+        <input type='text' name='lastName' class='form-control' placeholder='นามสกุล' required/>
13
+        <br>
14
+        <!-- 
15
+        <input type='text' name='idCard' class='form-control' placeholder='หมายเลขบัตรประชาชน' required /> 
16
+        <br> -->
17
+        <label>วันเกิด</label>
18
+        <input type='date' name='bd' class='form-control' placeholder='วันเกิด' required />
19
+        <br>
20
+        <textarea name='address' class='form-control' placeholder='ที่อยู่' required></textarea>  
21
+        <br>
22
+        <textarea name='comment' class='form-control' placeholder='ข้อความฝากถึงเจ้าหน้าที่'></textarea>  
23
+        <br>
24
+        <input type='tel' name='tel' class='form-control' placeholder='Tel.' required/>
25
+        <br>
26
+        <input type='text' name='line_id' class='form-control' placeholder='Line ID'/>
27
+        <br>
28
+        <label>อัพโหลดภาพ</label>
29
+        <input type="file" name='photo' accept="image/*;capture=camera" class='form-control'> </br>
30
+        <span class="glyphicon glyphicon-map-marker"></span>
31
+        <a class='btn btn-primary form-control' id="currentLocationBtn">
32
+        <i class="bi bi-geo-alt-fill"></i>
33
+            คลิกเพื่อแสดงตำแหน่ง</a><br><br>
34
+        <input type='text' class='form-control' name='geo' id='geoText' readonly/><br>
35
+        <input type='submit' value='ส่งข้อมูล' class='btn-success form-control'/><br><br>
36
+    </div>
37
+    </form>
38
+</div>
39
+{% endblock %}

+ 3 - 0
app/front/urls.py

@@ -6,5 +6,8 @@ from . import views
6 6
 urlpatterns = [
7 7
     path('', views.index, name='index'),
8 8
     path('tracking', views.tracking, name='tracking'),
9
+    path('tracking', views.tracking, name='tracking'),
9 10
     path('success', views.success, name='success'),
10 11
 ]
12
+
13
+app_name = 'front'

+ 16 - 0
app/front/views.py

@@ -23,6 +23,22 @@ def index(request):
23 23
         return redirect('success')
24 24
     return render(request, 'front/index.html')
25 25
 
26
+def news(request):
27
+    pass
28
+
29
+def forum(request):
30
+    pass
31
+
32
+def articles(request):
33
+    pass
34
+
35
+def market(request):
36
+    pass
37
+
38
+def mystore(requeset):
39
+    pass
40
+
41
+
26 42
 def success(request):
27 43
     return render(request, 'front/success.html')
28 44
 

+ 0 - 0
app/fruit/__init__.py


+ 67 - 0
app/fruit/admin.py

@@ -0,0 +1,67 @@
1
+from django.contrib import admin
2
+
3
+from django.contrib import admin
4
+from django.contrib.gis.admin import OSMGeoAdmin
5
+from .models import Store, ProductType, Product, Photo, ProductSKU
6
+# Register your models here.
7
+
8
+from django_google_maps import widgets as map_widgets
9
+from django_google_maps import fields as map_fields
10
+from django_json_widget.widgets import JSONEditorWidget
11
+from django.db import models
12
+from django.utils.html import format_html
13
+from django.conf import settings
14
+
15
+from dal import autocomplete
16
+from django import forms
17
+from mptt.admin import MPTTModelAdmin
18
+
19
+
20
+
21
+@admin.register(Store)
22
+class StoreAdmin(admin.ModelAdmin):
23
+    search_fields = ('name',)
24
+    list_display = ('name', 'address', 'geolocation')
25
+    fields = ('name', 'address_text',  'tel', 'line_id',  'email', 'address', 'geolocation',)
26
+    formfield_overrides = {
27
+        map_fields.AddressField: {'widget': map_widgets.GoogleMapsAddressWidget},
28
+    }
29
+
30
+
31
+
32
+@admin.register(ProductType)
33
+class ProductTypeAdmin(MPTTModelAdmin):
34
+    pass
35
+
36
+
37
+
38
+class PhotoInlineAdmin(admin.StackedInline):
39
+    model = Photo
40
+
41
+@admin.register(Product)
42
+class ProductAdmin(admin.ModelAdmin):
43
+    inlines = [PhotoInlineAdmin,]
44
+
45
+    formfield_overrides = {
46
+        map_fields.AddressField: {'widget': map_widgets.GoogleMapsAddressWidget},
47
+        models.JSONField: {'widget': JSONEditorWidget},
48
+    }
49
+    list_display = ["name", "created_at", "created_by"]
50
+
51
+    def save_model(self, request, obj, form, change):
52
+        if obj.created_by == None:
53
+            obj.created_by = request.user
54
+        #super().save_model(request, obj, form, change)
55
+        obj.modified_by = request.user
56
+        super().save_model(request, obj, form, change)
57
+
58
+@admin.register(ProductSKU)
59
+class ProductSKUAdmin(admin.ModelAdmin):
60
+    formfield_overrides = {
61
+        map_fields.AddressField: {'widget': map_widgets.GoogleMapsAddressWidget},
62
+        models.JSONField: {'widget': JSONEditorWidget},
63
+    }
64
+
65
+@admin.register(Photo)
66
+class PhotoAdmin(admin.ModelAdmin):
67
+    pass

+ 6 - 0
app/fruit/apps.py

@@ -0,0 +1,6 @@
1
+from django.apps import AppConfig
2
+
3
+
4
+class FruitConfig(AppConfig):
5
+    default_auto_field = 'django.db.models.BigAutoField'
6
+    name = 'fruit'

+ 6 - 0
app/fruit/middleware.py

@@ -0,0 +1,6 @@
1
+from .utils import set_current_user
2
+
3
+
4
+class CurrentUserMiddleware:
5
+    def process_request(self, request):
6
+        set_current_user(getattr(request, 'user', None))

+ 30 - 0
app/fruit/migrations/0001_initial.py

@@ -0,0 +1,30 @@
1
+# Generated by Django 3.2.5 on 2021-07-27 13:20
2
+
3
+from django.db import migrations, models
4
+import django_google_maps.fields
5
+
6
+
7
+class Migration(migrations.Migration):
8
+
9
+    initial = True
10
+
11
+    dependencies = [
12
+    ]
13
+
14
+    operations = [
15
+        migrations.CreateModel(
16
+            name='Store',
17
+            fields=[
18
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19
+                ('name', models.CharField(max_length=200)),
20
+                ('address_text', models.TextField(blank=True, null=True)),
21
+                ('address', django_google_maps.fields.AddressField(max_length=200)),
22
+                ('geolocation', django_google_maps.fields.GeoLocationField(max_length=100)),
23
+                ('tel', models.CharField(blank=True, max_length=100, null=True)),
24
+                ('line_id', models.CharField(blank=True, max_length=100, null=True)),
25
+                ('email', models.EmailField(blank=True, max_length=254, null=True)),
26
+                ('created_at', models.DateTimeField(auto_now_add=True, null=True)),
27
+                ('updated_at', models.DateTimeField(auto_now=True)),
28
+            ],
29
+        ),
30
+    ]

+ 30 - 0
app/fruit/migrations/0002_producttype.py

@@ -0,0 +1,30 @@
1
+# Generated by Django 3.2.5 on 2021-07-27 13:41
2
+
3
+from django.db import migrations, models
4
+import django.db.models.deletion
5
+import mptt.fields
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        ('fruit', '0001_initial'),
12
+    ]
13
+
14
+    operations = [
15
+        migrations.CreateModel(
16
+            name='ProductType',
17
+            fields=[
18
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19
+                ('name', models.CharField(max_length=50, unique=True)),
20
+                ('lft', models.PositiveIntegerField(editable=False)),
21
+                ('rght', models.PositiveIntegerField(editable=False)),
22
+                ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
23
+                ('level', models.PositiveIntegerField(editable=False)),
24
+                ('parent', mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='fruit.producttype')),
25
+            ],
26
+            options={
27
+                'abstract': False,
28
+            },
29
+        ),
30
+    ]

+ 19 - 0
app/fruit/migrations/0003_store_product_type.py

@@ -0,0 +1,19 @@
1
+# Generated by Django 3.2.5 on 2021-07-27 15:05
2
+
3
+from django.db import migrations, models
4
+import django.db.models.deletion
5
+
6
+
7
+class Migration(migrations.Migration):
8
+
9
+    dependencies = [
10
+        ('fruit', '0002_producttype'),
11
+    ]
12
+
13
+    operations = [
14
+        migrations.AddField(
15
+            model_name='store',
16
+            name='product_type',
17
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='fruit.producttype'),
18
+        ),
19
+    ]

+ 48 - 0
app/fruit/migrations/0004_auto_20210727_2220.py

@@ -0,0 +1,48 @@
1
+# Generated by Django 3.2.5 on 2021-07-27 15:20
2
+
3
+from django.conf import settings
4
+from django.db import migrations, models
5
+import django.db.models.deletion
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12
+        ('fruit', '0003_store_product_type'),
13
+    ]
14
+
15
+    operations = [
16
+        migrations.CreateModel(
17
+            name='Product',
18
+            fields=[
19
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20
+                ('name', models.CharField(max_length=200)),
21
+            ],
22
+        ),
23
+        migrations.RemoveField(
24
+            model_name='store',
25
+            name='product_type',
26
+        ),
27
+        migrations.CreateModel(
28
+            name='Profile',
29
+            fields=[
30
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
31
+                ('bio', models.TextField(blank=True, max_length=500)),
32
+                ('location', models.CharField(blank=True, max_length=30)),
33
+                ('birth_date', models.DateField(blank=True, null=True)),
34
+                ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
35
+            ],
36
+        ),
37
+        migrations.CreateModel(
38
+            name='Photo',
39
+            fields=[
40
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
41
+                ('name', models.CharField(max_length=200, unique=True)),
42
+                ('photo', models.ImageField(blank=True, upload_to='uploads/%Y/%m/%d/', verbose_name='Photo')),
43
+                ('created_at', models.DateTimeField(auto_now_add=True, null=True)),
44
+                ('updated_at', models.DateTimeField(auto_now=True)),
45
+                ('product', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='fruit.product')),
46
+            ],
47
+        ),
48
+    ]

+ 66 - 0
app/fruit/migrations/0005_auto_20210727_2243.py

@@ -0,0 +1,66 @@
1
+# Generated by Django 3.2.5 on 2021-07-27 15:43
2
+
3
+from django.conf import settings
4
+from django.db import migrations, models
5
+import django.db.models.deletion
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12
+        ('fruit', '0004_auto_20210727_2220'),
13
+    ]
14
+
15
+    operations = [
16
+        migrations.AddField(
17
+            model_name='photo',
18
+            name='created_by',
19
+            field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='photo_created', to=settings.AUTH_USER_MODEL),
20
+        ),
21
+        migrations.AddField(
22
+            model_name='photo',
23
+            name='modified_by',
24
+            field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='photo_modified', to=settings.AUTH_USER_MODEL),
25
+        ),
26
+        migrations.AddField(
27
+            model_name='product',
28
+            name='created_at',
29
+            field=models.DateTimeField(auto_now_add=True, null=True),
30
+        ),
31
+        migrations.AddField(
32
+            model_name='product',
33
+            name='created_by',
34
+            field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='product_created', to=settings.AUTH_USER_MODEL),
35
+        ),
36
+        migrations.AddField(
37
+            model_name='product',
38
+            name='description',
39
+            field=models.TextField(blank=True, null=True),
40
+        ),
41
+        migrations.AddField(
42
+            model_name='product',
43
+            name='modified_by',
44
+            field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='product_modified', to=settings.AUTH_USER_MODEL),
45
+        ),
46
+        migrations.AddField(
47
+            model_name='product',
48
+            name='price',
49
+            field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True),
50
+        ),
51
+        migrations.AddField(
52
+            model_name='product',
53
+            name='product_type',
54
+            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='fruit.producttype'),
55
+        ),
56
+        migrations.AddField(
57
+            model_name='product',
58
+            name='store',
59
+            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='fruit.store'),
60
+        ),
61
+        migrations.AddField(
62
+            model_name='product',
63
+            name='updated_at',
64
+            field=models.DateTimeField(auto_now=True),
65
+        ),
66
+    ]

+ 18 - 0
app/fruit/migrations/0006_product_details.py

@@ -0,0 +1,18 @@
1
+# Generated by Django 3.2.5 on 2021-07-27 15:58
2
+
3
+from django.db import migrations, models
4
+
5
+
6
+class Migration(migrations.Migration):
7
+
8
+    dependencies = [
9
+        ('fruit', '0005_auto_20210727_2243'),
10
+    ]
11
+
12
+    operations = [
13
+        migrations.AddField(
14
+            model_name='product',
15
+            name='details',
16
+            field=models.JSONField(blank=True, null=True),
17
+        ),
18
+    ]

+ 46 - 0
app/fruit/migrations/0007_auto_20210727_2312.py

@@ -0,0 +1,46 @@
1
+# Generated by Django 3.2.5 on 2021-07-27 16:12
2
+
3
+from django.conf import settings
4
+from django.db import migrations, models
5
+import django.db.models.deletion
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12
+        ('fruit', '0006_product_details'),
13
+    ]
14
+
15
+    operations = [
16
+        migrations.AddField(
17
+            model_name='product',
18
+            name='n_unit',
19
+            field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True),
20
+        ),
21
+        migrations.AddField(
22
+            model_name='product',
23
+            name='unit_name',
24
+            field=models.CharField(max_length=200, null=True),
25
+        ),
26
+        migrations.CreateModel(
27
+            name='ProductSKU',
28
+            fields=[
29
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
30
+                ('created_at', models.DateTimeField(auto_now_add=True, null=True)),
31
+                ('updated_at', models.DateTimeField(auto_now=True)),
32
+                ('sku', models.CharField(max_length=200)),
33
+                ('description', models.TextField(blank=True, null=True)),
34
+                ('price', models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True)),
35
+                ('details', models.JSONField(blank=True, null=True)),
36
+                ('n_unit', models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True)),
37
+                ('unit_name', models.CharField(max_length=200, null=True)),
38
+                ('created_by', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='productsku_created', to=settings.AUTH_USER_MODEL)),
39
+                ('modified_by', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='productsku_modified', to=settings.AUTH_USER_MODEL)),
40
+                ('product', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='fruit.product')),
41
+            ],
42
+            options={
43
+                'abstract': False,
44
+            },
45
+        ),
46
+    ]

+ 19 - 0
app/fruit/migrations/0008_product_code.py

@@ -0,0 +1,19 @@
1
+# Generated by Django 3.2.5 on 2021-07-27 16:18
2
+
3
+from django.db import migrations, models
4
+
5
+
6
+class Migration(migrations.Migration):
7
+
8
+    dependencies = [
9
+        ('fruit', '0007_auto_20210727_2312'),
10
+    ]
11
+
12
+    operations = [
13
+        migrations.AddField(
14
+            model_name='product',
15
+            name='code',
16
+            field=models.CharField(default='', max_length=200),
17
+            preserve_default=False,
18
+        ),
19
+    ]

+ 0 - 0
app/fruit/migrations/__init__.py


+ 133 - 0
app/fruit/models.py

@@ -0,0 +1,133 @@
1
+from django.db import models
2
+
3
+from django_google_maps import fields as map_fields
4
+from colorfield.fields import ColorField
5
+from smart_selects.db_fields import (
6
+    ChainedForeignKey,
7
+    ChainedManyToManyField,
8
+    GroupedForeignKey,
9
+)
10
+from django.db.models import Q
11
+import googlemaps
12
+from django.contrib.gis.geos import fromstr
13
+
14
+from django.conf import settings
15
+import csv
16
+import haversine as hs
17
+from mptt.models import MPTTModel, TreeForeignKey
18
+
19
+from django.contrib.auth.models import User
20
+from django.db.models.signals import post_save
21
+from django.dispatch import receiver
22
+from shaqfindbed.utils import get_current_user
23
+
24
+
25
+# Create your models here.
26
+
27
+class GenericModel(models.Model):
28
+    created_at = models.DateTimeField(auto_now_add=True, null=True)
29
+    updated_at = models.DateTimeField(auto_now=True)
30
+
31
+    created_by = models.ForeignKey(User, null=True, editable=False, related_name='%(class)s_created', on_delete=models.SET_NULL)
32
+    modified_by = models.ForeignKey(User, null=True, editable=False, related_name='%(class)s_modified', on_delete=models.SET_NULL)
33
+
34
+    '''
35
+    def save(self, *args, **kwargs):
36
+        user = get_current_user()
37
+        print("user "+user.is_authenticated())
38
+        if user and user.is_authenticated():
39
+            self.modified_by = user
40
+            if not self.id:
41
+                self.created_by = user
42
+        super(GenericModel, self).save(*args, **kwargs)
43
+    '''
44
+
45
+    class Meta:
46
+        abstract = True
47
+
48
+class Store(models.Model):
49
+    name = models.CharField(max_length=200)
50
+    address_text = models.TextField(blank=True, null=True)
51
+    #address = models.CharField(max_length=100)
52
+    address = map_fields.AddressField(max_length=200)
53
+    geolocation = map_fields.GeoLocationField(max_length=100)
54
+
55
+
56
+    tel = models.CharField(max_length=100, null=True, blank=True)
57
+    line_id = models.CharField(max_length=100, null=True, blank=True)
58
+    email = models.EmailField(null=True, blank=True)
59
+
60
+
61
+    created_at = models.DateTimeField(auto_now_add=True, null=True)
62
+    updated_at = models.DateTimeField(auto_now=True)
63
+
64
+    def __str__(self):
65
+        return f"{self.name}"
66
+
67
+
68
+
69
+class ProductType(MPTTModel):
70
+    name = models.CharField(max_length=50, unique=True)
71
+    parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')
72
+
73
+    class MPTTMeta:
74
+        order_insertion_by = ['name']
75
+
76
+
77
+    def __str__(self):
78
+        return f"{self.name}"
79
+
80
+
81
+class Product(GenericModel, models.Model ):
82
+    name = models.CharField(max_length=200)
83
+    code = models.CharField(max_length=200)
84
+    product_type = models.ForeignKey('ProductType', on_delete=models.SET_NULL, null=True)
85
+    description = models.TextField(blank=True, null=True)
86
+    store = models.ForeignKey('Store', on_delete=models.CASCADE, null=True, blank=False)
87
+    price = models.DecimalField(null=True, blank=True, decimal_places=2, max_digits=7)
88
+
89
+    details  = models.JSONField(null=True, blank=True)
90
+
91
+    n_unit = models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True)
92
+    unit_name = models.CharField(max_length=200, null=True)
93
+
94
+    def __str__(self):
95
+        return f"{self.name} {self.code}"
96
+
97
+class ProductSKU(GenericModel, models.Model ):
98
+    sku = models.CharField(max_length=200)
99
+    product  = models.ForeignKey('Product', on_delete=models.CASCADE, null=True)
100
+    description = models.TextField(blank=True, null=True)
101
+    price = models.DecimalField(null=True, blank=True, decimal_places=2, max_digits=7)
102
+
103
+    details  = models.JSONField(null=True, blank=True)
104
+
105
+    n_unit = models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True)
106
+    unit_name = models.CharField(max_length=200, null=True)
107
+
108
+class Photo(GenericModel, models.Model):
109
+    name = models.CharField(max_length=200, unique=True)
110
+    photo  = models.ImageField(upload_to="uploads/%Y/%m/%d/", blank=True, verbose_name="Photo")
111
+    product = models.ForeignKey('Product', on_delete=models.CASCADE, null=True)
112
+
113
+
114
+
115
+
116
+
117
+class Profile(models.Model):
118
+    user = models.OneToOneField(User, on_delete=models.CASCADE)
119
+    bio = models.TextField(max_length=500, blank=True)
120
+    location = models.CharField(max_length=30, blank=True)
121
+    birth_date = models.DateField(null=True, blank=True)
122
+
123
+@receiver(post_save, sender=User)
124
+def create_user_profile(sender, instance, created, **kwargs):
125
+    if created:
126
+        Profile.objects.create(user=instance)
127
+
128
+@receiver(post_save, sender=User)
129
+def save_user_profile(sender, instance, **kwargs):
130
+    try:
131
+        instance.profile.save()
132
+    except:
133
+        Profile.objects.create(user=instance)

+ 0 - 0
app/fruit/templates/fruit/index.html


+ 5 - 0
app/fruit/templates/fruit/mystore.html

@@ -0,0 +1,5 @@
1
+{% extends "front/base.html" %}
2
+{% load static %}
3
+{% block content %}
4
+<h1>My Store</h1>
5
+{% endblock %}

+ 21 - 0
app/fruit/templates/fruit/signup.html

@@ -0,0 +1,21 @@
1
+{% extends "front/base.html" %}
2
+{% load static %}
3
+{% block content %}
4
+  <h2>Sign up</h2>
5
+  <form method="post">
6
+    {% csrf_token %}
7
+    {% for field in form %}
8
+      <p>
9
+        {{ field.label_tag }}<br>
10
+        {{ field }}
11
+        {% if field.help_text %}
12
+          <small style="color: grey">{{ field.help_text }}</small>
13
+        {% endif %}
14
+        {% for error in field.errors %}
15
+          <p style="color: red">{{ error }}</p>
16
+        {% endfor %}
17
+      </p>
18
+    {% endfor %}
19
+    <button type="submit">Sign up</button>
20
+  </form>
21
+{% endblock %}

+ 3 - 0
app/fruit/tests.py

@@ -0,0 +1,3 @@
1
+from django.test import TestCase
2
+
3
+# Create your tests here.

+ 12 - 0
app/fruit/urls.py

@@ -0,0 +1,12 @@
1
+
2
+from django.urls import path
3
+from django.conf.urls import url
4
+from . import views
5
+
6
+urlpatterns = [
7
+    path('', views.index, name='index'),
8
+    path('mystore', views.mystore, name='mystore'),
9
+    path('signup', views.signup, name='signup'),
10
+]
11
+
12
+app_name = 'fruit'

+ 28 - 0
app/fruit/views.py

@@ -0,0 +1,28 @@
1
+#from django.shortcuts import render
2
+from django.shortcuts import render, redirect
3
+# Create your views here.
4
+from django.contrib.auth import login, authenticate
5
+from django.contrib.auth.forms import UserCreationForm
6
+from django.urls import reverse
7
+from django.contrib.auth.decorators import login_required
8
+
9
+def index(request):
10
+    return render(request, 'fruit/index.html')
11
+
12
+@login_required
13
+def mystore(request):
14
+    return render(request, 'fruit/mystore.html')
15
+
16
+def signup(request):
17
+    if request.method == 'POST':
18
+        form = UserCreationForm(request.POST)
19
+        if form.is_valid():
20
+            form.save()
21
+            username = form.cleaned_data.get('username')
22
+            raw_password = form.cleaned_data.get('password1')
23
+            user = authenticate(username=username, password=raw_password)
24
+            login(request, user)
25
+            return redirect('front:index')
26
+    else:
27
+        form = UserCreationForm()
28
+    return render(request, 'fruit/signup.html', {'form': form})

+ 0 - 0
app/requirements.txt


Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików

first commit · 3de4025a03 - Gogs: Simplico Git Service
Tum vor 2 Jahren
Commit
3de4025a03

+ 4 - 0
charger-simulator/.gitignore

@@ -0,0 +1,4 @@
1
+.idea
2
+/dist
3
+*.iml
4
+node_modules

+ 3 - 0
charger-simulator/.npmignore

@@ -0,0 +1,3 @@
1
+.idea
2
+node_modules
3
+*.iml

+ 7 - 0
charger-simulator/.prettierrc.js

@@ -0,0 +1,7 @@
1
+module.exports = {
2
+  printWidth: 100,
3
+  semi: false,
4
+  trailingComma: "es5",
5
+  bracketSpacing: false,
6
+  htmlWhitespaceSensitivity: "ignore",
7
+};

+ 21 - 0
charger-simulator/LICENSE

@@ -0,0 +1,21 @@
1
+MIT License
2
+
3
+Copyright (c) 2020 Vasyl Stashuk
4
+
5
+Permission is hereby granted, free of charge, to any person obtaining a copy
6
+of this software and associated documentation files (the "Software"), to deal
7
+in the Software without restriction, including without limitation the rights
8
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+copies of the Software, and to permit persons to whom the Software is
10
+furnished to do so, subject to the following conditions:
11
+
12
+The above copyright notice and this permission notice shall be included in all
13
+copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+SOFTWARE.

+ 88 - 0
charger-simulator/README.md

@@ -0,0 +1,88 @@
1
+EV charger simulator. Supports OCPP/J version 1.6 and OCPP/SOAP version 1.5. 
2
+Can be used as CLI program, or as library in any JS environment.
3
+
4
+# CLI usage
5
+
6
+## Getting started
7
+
8
+Install in project
9
+```
10
+yarn add vasyas/charger-simulator
11
+```
12
+
13
+or globally
14
+```
15
+yarn global add vasyas/charger-simulator
16
+```
17
+
18
+Then launch it with command
19
+```
20
+charger-simulator <URL to central system>
21
+```
22
+
23
+You can also run from a cloned git repository
24
+```
25
+yarn start <URL to central system>
26
+```
27
+
28
+On successfull launch, you will get this message
29
+
30
+```
31
+debug] OCPP connected
32
+[info] Connected to Central System
33
+[info] Supported keys:
34
+    Ctrl+C:   quit
35
+
36
+    Control connector 1
37
+    ---
38
+    a:        send Available status
39
+    p:        send Preparing status
40
+    c:        send Charging status
41
+    f:        send Finishing status
42
+```
43
+
44
+You can press keys to send connector status updates to central server.
45
+
46
+## CLI options
47
+```
48
+charger-simulator
49
+
50
+  Start OCPP charging station simulator, connect simulator to Central System
51
+  server.
52
+
53
+Options
54
+
55
+  -s, --csURL URL                 URL of the Central System server to connect to, ws://server.name/path.
56
+                                  This is also a default option.
57
+  -i, --chargerId ChargerId       OCPP ID to be used for simulating charger.
58
+                                  Default is 'test'.
59
+  -c, --connectorId ConnectorId   ID of the connector to send status when pressing keys.
60
+                                  Defaults to 1.
61
+  -t, --idTag idTag               ID Tag to start transaction.
62
+                                  Defaults to 123456.
63
+```
64
+
65
+## Default behavior
66
+
67
+By default simulator implements following OCPP operations.
68
+
69
+*RemoteStartTransaction*. Will successfully start new transaction. Call RemoteStartTransaction by server
70
+will result in StartTransaction and multiple MeterValues to be sent to central system.
71
+
72
+*RemoteStopTransaction*. Will stop running transaction.
73
+
74
+*GetConfiguration*. Return charge point configuration.
75
+*ChangeConfiguration*. Change charge point configuration.
76
+
77
+*ReserveNow, CancelReservation, Reset*. Return 'Accepted', but do nothing.
78
+
79
+All other methods are not implemented.
80
+
81
+# Library usage
82
+
83
+## Getting started
84
+TBD  
85
+
86
+## API
87
+TBD  
88
+

+ 2 - 0
charger-simulator/cli-index.js

@@ -0,0 +1,2 @@
1
+#!/usr/bin/env node
2
+require("./dist/chargerSimulatorCli.js")

Datei-Diff unterdrückt, da er zu groß ist
+ 2784 - 0
charger-simulator/package-lock.json


+ 37 - 0
charger-simulator/package.json

@@ -0,0 +1,37 @@
1
+{
2
+  "name": "charger-simulator",
3
+  "version": "1.0.2",
4
+  "main": "dist/index.js",
5
+  "bin": {
6
+    "charger-simulator": "cli-index.js"
7
+  },
8
+  "types": "dist/index.d.ts",
9
+  "scripts": {
10
+    "start": "ts-node src/chargerSimulatorCli.ts",
11
+    "prepublishOnly": "tsc",
12
+    "build": "tsc",
13
+    "docs": "typedoc --out docs ./src"
14
+  },
15
+  "license": "MIT",
16
+  "devDependencies": {
17
+    "@types/node": "^14.0.5",
18
+    "prettier": "^3.1.1",
19
+    "ts-node": "^10.9.2",
20
+    "typescript": "^5.3.3"
21
+  },
22
+  "repository": "https://github.com/vasyas/charger-simulator.git",
23
+  "author": "Vasyl Stashuk <vasyl@stashuk.com>",
24
+  "publishConfig": {
25
+    "access": "public"
26
+  },
27
+  "dependencies": {
28
+    "@push-rpc/core": "^1.8.2",
29
+    "@push-rpc/websocket": "^1.8.2",
30
+    "@types/ws": "^8.5.10",
31
+    "command-line-args": "^5.1.1",
32
+    "command-line-usage": "^6.1.0",
33
+    "strong-soap": "1.21.0",
34
+    "ws": "^8.15.1",
35
+    "xml2js": "^0.6.2"
36
+  }
37
+}

+ 239 - 0
charger-simulator/src/ChargerSimulator.ts

@@ -0,0 +1,239 @@
1
+import {createRpcClient} from "@push-rpc/core"
2
+import {wrapWebsocket} from "@push-rpc/websocket/dist/server"
3
+import * as WebSocket from "ws"
4
+import {log} from "./log"
5
+import {createCentralSystemClient, createChargePointServer} from "./soap/ocppSoap"
6
+
7
+export interface Config {
8
+  defaultHeartbeatIntervalSec?: number
9
+  chargePointVendor?: string
10
+  chargePointModel?: string
11
+  startDelayMs?: number
12
+  stopDelayMs?: number
13
+  keepAliveTimeoutMs?: number // set to null to disable pings
14
+  meterValuesIntervalSec?: number
15
+
16
+  centralSystemEndpoint: string
17
+  chargerIdentity: string
18
+  chargePointPort?: number
19
+}
20
+
21
+const defaultConfig: Partial<Config> = {
22
+  defaultHeartbeatIntervalSec: 30,
23
+  chargePointVendor: "Test",
24
+  chargePointModel: "1",
25
+  startDelayMs: 8 * 1000,
26
+  stopDelayMs: 8 * 1000,
27
+  keepAliveTimeoutMs: 50 * 1000,
28
+  meterValuesIntervalSec: 20,
29
+}
30
+
31
+let ws: WebSocket
32
+
33
+export class ChargerSimulator {
34
+  constructor(config: Config) {
35
+    this.config = {...defaultConfig, ...config}
36
+
37
+    this.configurationKeys = [
38
+      {key: "HeartBeatInterval", readonly: false, value: "" + config.defaultHeartbeatIntervalSec},
39
+      {key: "ResetRetries", readonly: false, value: "1"},
40
+      {key: "MeterValueSampleInterval", readonly: false, value: config.meterValuesIntervalSec},
41
+    ]
42
+  }
43
+
44
+  public async start() {
45
+    if (this.config.chargePointPort) {
46
+      await createChargePointServer(this.chargePoint, this.config.chargePointPort)
47
+      log.info(
48
+        `Started SOAP Charge Point server at http://localhost:${this.config.chargePointPort}/`
49
+      )
50
+
51
+      this.centralSystem = await createCentralSystemClient(
52
+        this.config.centralSystemEndpoint,
53
+        this.config.chargerIdentity,
54
+        `http://localhost:${this.config.chargePointPort}/`
55
+      )
56
+      log.info(`Will send messages to Central System at ${this.config.centralSystemEndpoint}`)
57
+    } else {
58
+      const {remote} = await createRpcClient(
59
+        async () => {
60
+          ws = new WebSocket(
61
+            this.config.centralSystemEndpoint + "/" + this.config.chargerIdentity,
62
+            "ocpp1.6"
63
+          )
64
+
65
+          return wrapWebsocket(ws)
66
+        },
67
+        {
68
+          local: this.chargePoint,
69
+          reconnect: true,
70
+          keepAliveTimeout: this.config.keepAliveTimeoutMs,
71
+
72
+          listeners: {
73
+            messageIn: (data) => {
74
+              log.debug("OCPP in", data)
75
+            },
76
+            messageOut: (data) => {
77
+              log.debug("OCPP out", data)
78
+            },
79
+            connected() {
80
+              log.debug("OCPP connected")
81
+            },
82
+            disconnected({code, reason}) {
83
+              log.debug("OCPP disconnected", {code, reason})
84
+            },
85
+            subscribed(subscriptions: number): void {},
86
+            unsubscribed(subscriptions: number): void {},
87
+          },
88
+        }
89
+      )
90
+
91
+      log.info(
92
+        `Connected to Central System at ${this.config.centralSystemEndpoint} using WebSocket`
93
+      )
94
+
95
+      this.centralSystem = remote
96
+    }
97
+
98
+    if (this.config.defaultHeartbeatIntervalSec) {
99
+      setInterval(() => {
100
+        this.centralSystem.Heartbeat()
101
+      }, this.config.defaultHeartbeatIntervalSec * 1000)
102
+    }
103
+  }
104
+
105
+  public startTransaction({connectorId, idTag}, delay) {
106
+    if (this.meterTimer) {
107
+      return false
108
+    }
109
+
110
+    setTimeout(
111
+      async () => {
112
+        this.transactionId = (
113
+          await this.centralSystem.StartTransaction({
114
+            connectorId,
115
+            idTag,
116
+            timestamp: new Date(),
117
+            meterStart: 0,
118
+          })
119
+        ).transactionId
120
+
121
+        this.charged = 0
122
+
123
+        this.meterTimer = setInterval(() => {
124
+          this.charged += Math.random() > 0.66 ? 30 : 20 // 26.6 W / 10s avg = 9.36 Kw
125
+
126
+          this.centralSystem.MeterValues({
127
+            connectorId,
128
+            transactionId: this.transactionId,
129
+            meterValue: [
130
+              {
131
+                timestamp: new Date(),
132
+                sampledValue: [
133
+                  {
134
+                    value: "" + this.charged,
135
+                    measurand: "Energy.Active.Import.Register",
136
+                    unit: "Wh",
137
+                  },
138
+                  {
139
+                    value: "38",
140
+                    measurand: "SoC",
141
+                    unit: "Percent",
142
+                  },
143
+                ],
144
+              },
145
+            ],
146
+          })
147
+        }, this.config.meterValuesIntervalSec * 1000)
148
+      },
149
+      delay ? this.config.startDelayMs : 0
150
+    )
151
+
152
+    return true
153
+  }
154
+
155
+  public stopTransaction(delay) {
156
+    if (!this.meterTimer) {
157
+      return false
158
+    }
159
+
160
+    clearInterval(this.meterTimer)
161
+
162
+    setTimeout(
163
+      async () => {
164
+        await this.centralSystem.StopTransaction({
165
+          transactionId: this.transactionId,
166
+          timestamp: new Date(),
167
+          meterStop: this.charged,
168
+        })
169
+
170
+        this.meterTimer = null
171
+        this.transactionId = null
172
+      },
173
+      delay ? this.config.stopDelayMs : 0
174
+    )
175
+
176
+    return true
177
+  }
178
+
179
+  disconnect() {
180
+    ws.close()
181
+  }
182
+
183
+  public centralSystem = null
184
+
185
+  private config: Config = null
186
+  private meterTimer = null
187
+  private charged = 0
188
+  private configurationKeys = []
189
+  private transactionId = null
190
+  private chargePoint = {
191
+    RemoteStartTransaction: async (req) => {
192
+      return {
193
+        status: this.startTransaction(req, true) ? "Accepted" : "Rejected",
194
+        // status: "Rejected",
195
+      }
196
+    },
197
+
198
+    RemoteStopTransaction: async (req) => {
199
+      return {
200
+        status: this.stopTransaction(true) ? "Accepted" : "Rejected",
201
+      }
202
+    },
203
+
204
+    GetConfiguration: async (req) => {
205
+      await new Promise((r) => setTimeout(r, 2000))
206
+
207
+      return {configurationKey: this.configurationKeys}
208
+    },
209
+    ChangeConfiguration: async (req) => {
210
+      for (let i = 0; i < this.configurationKeys.length; i++) {
211
+        if (this.configurationKeys[i].key == req.key) {
212
+          this.configurationKeys[i].value = "" + req.value
213
+        }
214
+      }
215
+
216
+      return {status: "Accepted"}
217
+    },
218
+
219
+    ReserveNow: async (req) => {
220
+      return {status: "Accepted"}
221
+    },
222
+
223
+    CancelReservation: async (req) => {
224
+      return {status: "Accepted"}
225
+    },
226
+
227
+    Reset: async (req) => {
228
+      return {status: "Accepted"}
229
+    },
230
+
231
+    TriggerMessage: async (req) => {
232
+      return {status: "Accepted"}
233
+    },
234
+
235
+    UpdateFirmware: async (req) => {
236
+      return {status: "Accepted"}
237
+    },
238
+  }
239
+}

+ 156 - 0
charger-simulator/src/chargerSimulatorCli.ts

@@ -0,0 +1,156 @@
1
+import * as commandLineArgs from "command-line-args"
2
+import * as commandLineUsage from "command-line-usage"
3
+import * as readline from "readline"
4
+
5
+import {log} from "./log"
6
+import {ChargerSimulator} from "./ChargerSimulator"
7
+
8
+const optionList = [
9
+  {
10
+    name: "csURL",
11
+    type: String,
12
+    description:
13
+      "URL of the Central System server to connect to, ws://server.name/path.\nThis is also a default option.",
14
+    typeLabel: "{underline URL}",
15
+    alias: "s",
16
+    defaultOption: true,
17
+  },
18
+  {
19
+    name: "cpPort",
20
+    type: Number,
21
+    description:
22
+      "Port number to bind ChargePoint SOAP service. If specified, emulator will use SOAP protocol to connect to Central System, otherwise, WebSocket will be used",
23
+    typeLabel: "{underline Number}",
24
+    alias: "p",
25
+  },
26
+  {
27
+    name: "chargerId",
28
+    type: String,
29
+    description: "OCPP ID to be used for simulating charger.\nDefault is 'test'.",
30
+    typeLabel: "{underline ChargerId}",
31
+    alias: "i",
32
+    defaultValue: "test",
33
+  },
34
+  {
35
+    name: "connectorId",
36
+    type: String,
37
+    description: "ID of the connector to send status when pressing keys.\nDefaults to 1.",
38
+    typeLabel: "{underline ConnectorId}",
39
+    alias: "c",
40
+    defaultValue: 1,
41
+  },
42
+  {
43
+    name: "idTag",
44
+    type: String,
45
+    description: "ID Tag to start transaction.\nDefaults to 123456.",
46
+    typeLabel: "{underline idTag}",
47
+    alias: "t",
48
+    defaultValue: "12345678",
49
+  },
50
+]
51
+
52
+const usageSections = [
53
+  {
54
+    header: "charger-simulator",
55
+    content: "Start OCPP charging station simulator, connect simulator to Central System server.",
56
+  },
57
+  {
58
+    header: "Options",
59
+    optionList,
60
+  },
61
+]
62
+
63
+;(async () => {
64
+  const {connectorId, csURL, cpPort, chargerId, idTag} = commandLineArgs(optionList)
65
+
66
+  if (!connectorId || !csURL || !chargerId) {
67
+    const usage = commandLineUsage(usageSections)
68
+    console.log(usage)
69
+    return
70
+  }
71
+
72
+  log.info("Starting charger simulator", {
73
+    csURL,
74
+    connectorId,
75
+    chargerId,
76
+    idTag,
77
+  })
78
+
79
+  const simulator = new ChargerSimulator({
80
+    centralSystemEndpoint: csURL,
81
+    chargerIdentity: chargerId,
82
+    chargePointPort: cpPort,
83
+  })
84
+  await simulator.start()
85
+
86
+  log.info(`Supported keys:
87
+    Ctrl+C:   quit
88
+    
89
+    --
90
+    b:        send BootNotification
91
+    d:        send DataTransfer
92
+    i:        disconnect from Central System
93
+    
94
+    Connector ${connectorId} status
95
+    ---
96
+    a:        send Available status 
97
+    p:        send Preparing status
98
+    c:        send Charging status
99
+    e:        send SuspendedEV status
100
+    f:        send Finishing status
101
+    
102
+    Transaction on connector ${connectorId}, tag ${idTag}
103
+    --
104
+    u:        Authorize
105
+    s:        StartTransaction
106
+    t:        StopTransaction
107
+  `)
108
+
109
+  async function sendStatus(status: string) {
110
+    await simulator.centralSystem.StatusNotification({
111
+      connectorId: connectorId,
112
+      errorCode: "NoError",
113
+      status,
114
+    })
115
+  }
116
+
117
+  const commands = {
118
+    b: () =>
119
+      simulator.centralSystem.BootNotification({
120
+        chargePointVendor: "OC",
121
+        chargePointModel: "OCX",
122
+      }),
123
+    d: () =>
124
+      simulator.centralSystem.DataTransfer({
125
+        vendorId: "Emulator",
126
+        messageId: "MessageID",
127
+        data: "Data",
128
+      }),
129
+
130
+    i: () => simulator.disconnect(),
131
+
132
+    a: () => sendStatus("Available"),
133
+    p: () => sendStatus("Preparing"),
134
+    c: () => sendStatus("Charging"),
135
+    e: () => sendStatus("SuspendedEV"),
136
+    f: () => sendStatus("Finishing"),
137
+
138
+    u: () => simulator.centralSystem.Authorize({idTag}),
139
+    s: () => simulator.startTransaction({idTag, connectorId}, false),
140
+    t: () => simulator.stopTransaction(false),
141
+  }
142
+
143
+  readline.emitKeypressEvents(process.stdin)
144
+  process.stdin.setRawMode(true)
145
+
146
+  process.stdin.on("keypress", (ch, key) => {
147
+    if (key.ctrl && key.name === "c") {
148
+      process.exit()
149
+    }
150
+
151
+    if (ch) {
152
+      const command = commands[ch]
153
+      command && command()
154
+    }
155
+  })
156
+})()

+ 5 - 0
charger-simulator/src/log.ts

@@ -0,0 +1,5 @@
1
+export const log = {
2
+  info: (s, ...rest) => console.log(`[info] ${s}`, ...rest),
3
+  debug: (s, ...rest) => console.log(`[debug] ${s}`, ...rest),
4
+  error: (s, ...rest) => console.log(`[error] ${s}`, ...rest),
5
+}

+ 160 - 0
charger-simulator/src/soap/nodeSoapUtils.ts

@@ -0,0 +1,160 @@
1
+import * as fs from "fs"
2
+import * as http from "http"
3
+import {soap} from "strong-soap"
4
+import {convertDateToString, convertStringToDate, ISO8601, ISO8601_secs, proxify, proxy,} from "./serverUtils"
5
+import {prettyPrintXml} from "./xmlUtils"
6
+import {log} from "../log"
7
+
8
+let server = null
9
+
10
+export function createServer({
11
+  wsdlFile,
12
+  soapService,
13
+  path,
14
+  port,
15
+}): Promise<string> {
16
+  const wsdl = fs.readFileSync(wsdlFile, "utf8")
17
+
18
+  return new Promise((resolve, reject) => {
19
+    const createServer = () => {
20
+      server = http.createServer((request, response) => {
21
+        response.end(`404: Not Found: ${request.url}`)
22
+      })
23
+
24
+      server.listen(port, () => {
25
+        log.info(`OCPP Server is listening on port ${port}`)
26
+        resolve(server)
27
+      })
28
+
29
+      const soapServer = soap.listen(server, path, soapService, wsdl)
30
+      soapServer.log = soapServerLog
31
+    }
32
+
33
+    if (server) {
34
+      server.close(createServer)
35
+    } else {
36
+      createServer()
37
+    }
38
+  })
39
+}
40
+
41
+export async function createClient(chargeBoxId, wsdlFile, endpoint): Promise<any> {
42
+  return new Promise((resolve, reject) => {
43
+    soap.createClient(wsdlFile, {endpoint, attributesKey: "attributes"}, (err, client) => {
44
+      if (err) reject(err)
45
+      else {
46
+        const keys = getClientKeys(client)
47
+
48
+        promisifyClient(chargeBoxId, client, keys)
49
+        resolve(client)
50
+      }
51
+    })
52
+  })
53
+}
54
+
55
+export function getClientKeys(client): string[] {
56
+  const d = client.describe()
57
+
58
+  const service = d[Object.keys(d)[0]]
59
+  const binding = service[Object.keys(service)[0]]
60
+
61
+  return Object.keys(binding)
62
+}
63
+
64
+function uncap(s) {
65
+  return s[0].toLowerCase() + s.substring(1)
66
+}
67
+
68
+function promisifyClient(chargeBoxId, target, keys) {
69
+  target.on("request", (envelope) => {
70
+    logOcppRequest(chargeBoxId, envelope)
71
+  })
72
+
73
+  proxify(
74
+    target,
75
+    (impl, key, message) => {
76
+      return new Promise((resolve, reject) => {
77
+        const inputMessage = wrapMessage(key, soapDateToString(message), "Request")
78
+
79
+        impl(inputMessage, (err, result, envelope) => {
80
+          logOcppResponse(chargeBoxId, envelope)
81
+
82
+          if (err) {
83
+            const e = err.Fault ? err.Fault : err
84
+            log.error(`Failed to call ${key}`, e)
85
+            reject(e)
86
+          } else {
87
+            resolve(result)
88
+          }
89
+        })
90
+      })
91
+    },
92
+    keys
93
+  )
94
+}
95
+
96
+// see https://github.com/strongloop/strong-soap/issues/49
97
+// see https://github.com/strongloop/strong-soap/issues/113
98
+function wrapMessage(operationName, message, wrapperName) {
99
+  return {
100
+    [uncap(operationName) + wrapperName]: message,
101
+  }
102
+}
103
+
104
+function soapDateToString(message) {
105
+  return convertDateToString(message, (d) => d.toISOString())
106
+}
107
+
108
+function soapStringToDate(message) {
109
+  return convertStringToDate(
110
+    message,
111
+    (s) => ISO8601.test(s) || ISO8601_secs.test(s),
112
+    (s) => new Date(s)
113
+  )
114
+}
115
+
116
+/** Convert promise-based WS impl to node-soap compat, also date fixes */
117
+export function promisifyServer(target, keys) {
118
+  return proxy(
119
+    target,
120
+    (impl, key, args, callback, headers, req) => {
121
+      const promise = impl(soapStringToDate(args), headers, req)
122
+
123
+      promise
124
+        .then((r) => {
125
+          callback(wrapMessage(key, soapDateToString(r), "Response"))
126
+        })
127
+        .catch((e) => {
128
+          log.error(`Failed to serve ${key}`, e.Fault ? e.Fault : e)
129
+
130
+          callback(null, {
131
+            Fault: {
132
+              Code: {
133
+                Value: "soap:Sender",
134
+                Subcode: {value: "rpc:BadArguments"},
135
+              },
136
+              Reason: {Text: "Processing Error"},
137
+            },
138
+          })
139
+        })
140
+    },
141
+    keys
142
+  )
143
+}
144
+
145
+function soapServerLog(type, data) {
146
+  if (type == "error") log.error(data)
147
+}
148
+
149
+export async function logOcppRequest(chargeBoxId, envelope) {
150
+  if (process.env.noRequestLogging) return
151
+  const details = await prettyPrintXml(envelope)
152
+  log.debug("OCPP out", details)
153
+
154
+}
155
+
156
+export async function logOcppResponse(chargeBoxId, envelope) {
157
+  if (process.env.noRequestLogging) return
158
+  const details = await prettyPrintXml(envelope)
159
+  log.debug("OCPP in", details)
160
+}

+ 81 - 0
charger-simulator/src/soap/ocppSoap.ts

@@ -0,0 +1,81 @@
1
+// SOAP bindings for OCPP 1.5
2
+
3
+import * as path from "path"
4
+import * as UUID from "uuid-js"
5
+import {createClient, createServer, getClientKeys, promisifyServer} from "./nodeSoapUtils"
6
+import { proxy } from "./serverUtils"
7
+
8
+export function createChargePointServer(target, port) {
9
+  const keys = Object.keys(target)
10
+
11
+  const a = promisifyServer(target, keys)
12
+
13
+  const soapService = {
14
+    ChargePointService: {
15
+      ChargePointServiceSoap12: a,
16
+    },
17
+  }
18
+
19
+  return createServer({
20
+    wsdlFile: path.resolve(__dirname, "wsdl", "15", "ocpp_chargepointservice_1.5_final.wsdl"),
21
+
22
+    path: "/",
23
+    port,
24
+    soapService,
25
+  })
26
+}
27
+
28
+export async function createCentralSystemClient(
29
+  endpoint,
30
+  chargeBoxIdentity,
31
+  chargeBoxEndpoint
32
+): Promise<any> {
33
+  const client = await createClient(
34
+    chargeBoxIdentity,
35
+    path.resolve(__dirname, "wsdl", "15", "ocpp_centralsystemservice_1.5_final.wsdl"),
36
+    endpoint
37
+  )
38
+  return withSetWsAddressingHeaders(
39
+    client,
40
+    chargeBoxIdentity,
41
+    chargeBoxEndpoint,
42
+    endpoint,
43
+    getClientKeys(client),
44
+    "urn://Ocpp/Cs/2012/06/"
45
+  )
46
+}
47
+
48
+function withSetWsAddressingHeaders(
49
+  target,
50
+  chargeBoxIdentity,
51
+  fromEndPoint,
52
+  toEndPoint,
53
+  keys,
54
+  idNs
55
+) {
56
+  const wsa = `xmlns:a="http://www.w3.org/2005/08/addressing"`
57
+
58
+  return proxy(
59
+    target,
60
+    (impl, key, ...args) => {
61
+      target.clearSoapHeaders()
62
+
63
+      const action = "/" + key
64
+      const uuid = UUID.create()
65
+
66
+      target.addSoapHeader(
67
+        `<chargeBoxIdentity xmlns="${idNs}">${chargeBoxIdentity}</chargeBoxIdentity>`
68
+      )
69
+      target.addSoapHeader(`<a:MessageID ${wsa}>urn:uuid:${uuid}</a:MessageID>`)
70
+      target.addSoapHeader(`<a:From ${wsa}><a:Address>${fromEndPoint}</a:Address></a:From>`)
71
+      target.addSoapHeader(
72
+        `<a:ReplyTo ${wsa}><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></a:ReplyTo>`
73
+      )
74
+      target.addSoapHeader(`<a:To ${wsa}>${toEndPoint}</a:To>`)
75
+      target.addSoapHeader(`<a:Action ${wsa} soap:mustUnderstand="1">${action}</a:Action>`)
76
+
77
+      return impl(...args)
78
+    },
79
+    keys
80
+  )
81
+}

+ 77 - 0
charger-simulator/src/soap/serverUtils.ts

@@ -0,0 +1,77 @@
1
+export type Invoker = (impl, name, ...args) => any
2
+
3
+export function proxy<T extends object>(target: T, invoker: Invoker, keys: string[]): T {
4
+  const r = {...(target as any)}
5
+
6
+  keys.forEach((key) => {
7
+    r[key] = function (...args) {
8
+      const impl = target[key]
9
+      return invoker(impl, key, ...args)
10
+    }
11
+  })
12
+
13
+  return r as any
14
+}
15
+
16
+export function proxify(target: object, invoker: Invoker, keys: string[]): void {
17
+  keys.forEach((key) => {
18
+    const impl = target[key]
19
+
20
+    target[key] = function (...args) {
21
+      return invoker(impl, key, ...args)
22
+    }
23
+  })
24
+}
25
+
26
+export function convertDateToString(message, format) {
27
+  if (!message) return message
28
+
29
+  Object.keys(message).forEach((key) => {
30
+    const prop = message[key]
31
+
32
+    if (typeof prop != "object") return
33
+
34
+    if (prop instanceof Date) {
35
+      message[key] = format(prop)
36
+      return
37
+    }
38
+
39
+    if (!Array.isArray(prop)) return convertDateToString(prop, format)
40
+
41
+    for (let i = 0; i < prop.length; i++) {
42
+      convertDateToString(prop[i], format)
43
+    }
44
+  })
45
+
46
+  return message
47
+}
48
+
49
+export function convertStringToDate(message, match, parse) {
50
+  if (!message) return message
51
+
52
+  Object.keys(message).forEach((key) => {
53
+    const prop = message[key]
54
+
55
+    if (!prop) return
56
+
57
+    if (typeof prop == "string") {
58
+      if (match(prop)) message[key] = parse(prop)
59
+
60
+      return
61
+    }
62
+
63
+    if (typeof prop != "object") return
64
+
65
+    if (!Array.isArray(prop)) return convertStringToDate(prop, match, parse)
66
+
67
+    for (let i = 0; i < prop.length; i++) {
68
+      convertStringToDate(prop[i], match, parse)
69
+    }
70
+  })
71
+
72
+  return message
73
+}
74
+
75
+export const ISO8601 = /^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d.\d\d\dZ$/
76
+export const ISO8601_secs = /^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ$/
77
+export const ISO8601_date = /^\d\d\d\d-\d\d-\d\d$/

+ 757 - 0
charger-simulator/src/soap/wsdl/15/ocpp_centralsystemservice_1.5_final.wsdl

@@ -0,0 +1,757 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<!--
3
+OCPP Central System service description
4
+Version 1.5 draft for approval (1.5 RC4)
5
+
6
+Copyright e-laad.nl. Free to use without modifications.
7
+
8
+See: http://ev-services.net/ocpp/
9
+-->
10
+<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
11
+                  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
12
+                  xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
13
+                  xmlns:s="http://www.w3.org/2001/XMLSchema"
14
+                  xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
15
+                  xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
16
+                  xmlns:tns="urn://Ocpp/Cs/2012/06/"
17
+                  targetNamespace="urn://Ocpp/Cs/2012/06/"
18
+                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
19
+                  xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
20
+                  xmlns:wsa="http://www.w3.org/2005/08/addressing"
21
+                  xmlns:wsp="http://schemas.xmlsoapf.org/ws/2004/09/policy"
22
+                  xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
23
+
24
+    <wsp:Policy wsu:Id="ServicePolicy">
25
+        <wsp:ExactlyOne>
26
+            <wsp:All>
27
+                <wsaw:UsingAddressing/>
28
+            </wsp:All>
29
+        </wsp:ExactlyOne>
30
+    </wsp:Policy>
31
+
32
+    <wsdl:types>
33
+        <s:schema targetNamespace="urn://Ocpp/Cs/2012/06/" elementFormDefault="qualified">
34
+
35
+            <!-- Begin of types shared with ChargePointService -->
36
+            <s:simpleType name="IdToken">
37
+                <s:annotation>
38
+                    <s:documentation>Type of string defining identification token, e.g. RFID or credit card number. To be treated as case insensitive.</s:documentation>
39
+                </s:annotation>
40
+                <s:restriction base="s:string">
41
+                    <s:maxLength value="20"/>
42
+                </s:restriction>
43
+            </s:simpleType>
44
+
45
+            <s:simpleType name="AuthorizationStatus">
46
+                <s:annotation>
47
+                    <s:documentation>Defines the authorization-status-value</s:documentation>
48
+                </s:annotation>
49
+                <s:restriction base="s:string">
50
+                    <s:enumeration value="Accepted"/>
51
+                    <s:enumeration value="Blocked"/>
52
+                    <s:enumeration value="Expired"/>
53
+                    <s:enumeration value="Invalid"/>
54
+                    <s:enumeration value="ConcurrentTx"/>
55
+                </s:restriction>
56
+            </s:simpleType>
57
+
58
+            <s:complexType name="IdTagInfo">
59
+                <s:sequence>
60
+                    <s:element name="status" type="tns:AuthorizationStatus" minOccurs="1" maxOccurs="1"/>
61
+                    <s:element name="expiryDate" type="s:dateTime" minOccurs="0" maxOccurs="1"/>
62
+                    <s:element name="parentIdTag" type="tns:IdToken" minOccurs="0" maxOccurs="1"/>
63
+                </s:sequence>
64
+            </s:complexType>
65
+            <!-- End of types shared with ChargePointService -->
66
+
67
+            <s:simpleType name="ChargeBoxSerialNumber">
68
+                <s:annotation>
69
+                    <s:documentation>String type of max 25 chars that is to be treated as case insensitive.</s:documentation>
70
+                </s:annotation>
71
+                <s:restriction base="s:string">
72
+                    <s:maxLength value="25"/>
73
+                </s:restriction>
74
+            </s:simpleType>
75
+
76
+            <s:simpleType name="ChargePointModel">
77
+                <s:annotation>
78
+                    <s:documentation>String type of max 20 chars that is to be treated as case insensitive.</s:documentation>
79
+                </s:annotation>
80
+                <s:restriction base="s:string">
81
+                    <s:maxLength value="20"/>
82
+                </s:restriction>
83
+            </s:simpleType>
84
+
85
+            <s:simpleType name="ChargePointSerialNumber">
86
+                <s:annotation>
87
+                    <s:documentation>String type of max 25 chars that is to be treated as case insensitive.</s:documentation>
88
+                </s:annotation>
89
+                <s:restriction base="s:string">
90
+                    <s:maxLength value="25"/>
91
+                </s:restriction>
92
+            </s:simpleType>
93
+
94
+            <s:simpleType name="ChargePointVendor">
95
+                <s:annotation>
96
+                    <s:documentation>String type of max 20 chars that is to be treated as case insensitive.</s:documentation>
97
+                </s:annotation>
98
+                <s:restriction base="s:string">
99
+                    <s:maxLength value="20"/>
100
+                </s:restriction>
101
+            </s:simpleType>
102
+
103
+            <s:simpleType name="FirmwareVersion">
104
+                <s:annotation>
105
+                    <s:documentation>String type of max 50 chars that is to be treated as case insensitive.</s:documentation>
106
+                </s:annotation>
107
+                <s:restriction base="s:string">
108
+                    <s:maxLength value="50"/>
109
+                </s:restriction>
110
+            </s:simpleType>
111
+
112
+            <s:simpleType name="IccidString">
113
+                <s:annotation>
114
+                    <s:documentation>String type of max 20 chars that is to be treated as case insensitive.</s:documentation>
115
+                </s:annotation>
116
+                <s:restriction base="s:string">
117
+                    <s:maxLength value="20"/>
118
+                </s:restriction>
119
+            </s:simpleType>
120
+
121
+            <s:simpleType name="ImsiString">
122
+                <s:annotation>
123
+                    <s:documentation>String type of max 20 chars that is to be treated as case insensitive.</s:documentation>
124
+                </s:annotation>
125
+                <s:restriction base="s:string">
126
+                    <s:maxLength value="20"/>
127
+                </s:restriction>
128
+            </s:simpleType>
129
+
130
+            <s:simpleType name="MeterSerialNumber">
131
+                <s:annotation>
132
+                    <s:documentation>String type of max 25 chars that is to be treated as case insensitive.</s:documentation>
133
+                </s:annotation>
134
+                <s:restriction base="s:string">
135
+                    <s:maxLength value="25"/>
136
+                </s:restriction>
137
+            </s:simpleType>
138
+
139
+            <s:simpleType name="MeterType">
140
+                <s:annotation>
141
+                    <s:documentation>String type of max 25 chars that is to be treated as case insensitive.</s:documentation>
142
+                </s:annotation>
143
+                <s:restriction base="s:string">
144
+                    <s:maxLength value="25"/>
145
+                </s:restriction>
146
+            </s:simpleType>
147
+
148
+            <s:complexType name="AuthorizeRequest">
149
+                <s:annotation>
150
+                    <s:documentation>Defines the Authorize.req PDU</s:documentation>
151
+                </s:annotation>
152
+                <s:sequence>
153
+                    <s:element name="idTag" type="tns:IdToken" minOccurs="1" maxOccurs="1"/>
154
+                </s:sequence>
155
+            </s:complexType>
156
+
157
+            <s:complexType name="AuthorizeResponse">
158
+                <s:annotation>
159
+                    <s:documentation>Defines the Authorize.conf PDU</s:documentation>
160
+                </s:annotation>
161
+                <s:sequence>
162
+                    <s:element name="idTagInfo" type="tns:IdTagInfo" minOccurs="1" maxOccurs="1"/>
163
+                </s:sequence>
164
+            </s:complexType>
165
+
166
+            <s:complexType name="StartTransactionRequest">
167
+                <s:annotation>
168
+                    <s:documentation>Defines the StartTransaction.req PDU</s:documentation>
169
+                </s:annotation>
170
+                <s:sequence>
171
+                    <s:element name="connectorId" type="s:int" minOccurs="1" maxOccurs="1"/>
172
+                    <s:element name="idTag" type="tns:IdToken" minOccurs="1" maxOccurs="1"/>
173
+                    <s:element name="timestamp" type="s:dateTime" minOccurs="1" maxOccurs="1"/>
174
+                    <s:element name="meterStart" type="s:int" minOccurs="1" maxOccurs="1"/>
175
+                    <s:element name="reservationId" type="s:int" minOccurs="0" maxOccurs="1"/>
176
+                </s:sequence>
177
+            </s:complexType>
178
+
179
+            <s:complexType name="StartTransactionResponse">
180
+                <s:annotation>
181
+                    <s:documentation>Defines the StartTransaction.conf PDU</s:documentation>
182
+                </s:annotation>
183
+                <s:sequence>
184
+                    <s:element name="transactionId" type="s:int" minOccurs="1" maxOccurs="1"/>
185
+                    <s:element name="idTagInfo" type="tns:IdTagInfo" minOccurs="1" maxOccurs="1"/>
186
+                </s:sequence>
187
+            </s:complexType>
188
+
189
+            <s:complexType name="TransactionData">
190
+                <s:annotation>
191
+                    <s:documentation>This contains transaction usage details relevant for billing purposes in StopTransaction.req PDU</s:documentation>
192
+                </s:annotation>
193
+                <s:sequence>
194
+                    <s:element name="values" type="tns:MeterValue" minOccurs="0" maxOccurs="unbounded"/>
195
+                </s:sequence>
196
+            </s:complexType>
197
+
198
+            <s:complexType name="StopTransactionRequest">
199
+                <s:annotation>
200
+                    <s:documentation>Defines the StopTransaction.req PDU</s:documentation>
201
+                </s:annotation>
202
+                <s:sequence>
203
+                    <s:element name="transactionId" type="s:int" minOccurs="1" maxOccurs="1"/>
204
+                    <s:element name="idTag" type="tns:IdToken" minOccurs="0" maxOccurs="1"/>
205
+                    <s:element name="timestamp" type="s:dateTime" minOccurs="1" maxOccurs="1"/>
206
+                    <s:element name="meterStop" type="s:int" minOccurs="1" maxOccurs="1"/>
207
+                    <s:element name="transactionData" type="tns:TransactionData" minOccurs="0" maxOccurs="unbounded"/>
208
+                </s:sequence>
209
+            </s:complexType>
210
+
211
+            <s:complexType name="StopTransactionResponse">
212
+                <s:annotation>
213
+                    <s:documentation>Defines the StopTransaction.conf PDU</s:documentation>
214
+                </s:annotation>
215
+                <s:sequence>
216
+                    <s:element name="idTagInfo" type="tns:IdTagInfo" minOccurs="0" maxOccurs="1"/>
217
+                </s:sequence>
218
+            </s:complexType>
219
+
220
+            <s:complexType name="HeartbeatRequest">
221
+                <s:annotation>
222
+                    <s:documentation>Defines the Heartbeat.req PDU</s:documentation>
223
+                </s:annotation>
224
+            </s:complexType>
225
+
226
+            <s:complexType name="HeartbeatResponse">
227
+                <s:annotation>
228
+                    <s:documentation>Defines the Heartbeat.conf PDU</s:documentation>
229
+                </s:annotation>
230
+                <s:sequence>
231
+                    <s:element name="currentTime" type="s:dateTime" minOccurs="1" maxOccurs="1"/>
232
+                </s:sequence>
233
+            </s:complexType>
234
+
235
+            <s:simpleType name="ReadingContext">
236
+                <s:restriction base="s:string">
237
+                    <s:enumeration value="Interruption.Begin"/>
238
+                    <s:enumeration value="Interruption.End"/>
239
+                    <s:enumeration value="Sample.Clock"/>
240
+                    <s:enumeration value="Sample.Periodic"/>
241
+                    <s:enumeration value="Transaction.Begin"/>
242
+                    <s:enumeration value="Transaction.End"/>
243
+                </s:restriction>
244
+            </s:simpleType>
245
+
246
+            <s:simpleType name="Measurand">
247
+                <s:restriction base="s:string">
248
+                    <s:enumeration value="Energy.Active.Export.Register"/>
249
+                    <s:enumeration value="Energy.Active.Import.Register"/>
250
+                    <s:enumeration value="Energy.Reactive.Export.Register"/>
251
+                    <s:enumeration value="Energy.Reactive.Import.Register"/>
252
+                    <s:enumeration value="Energy.Active.Export.Interval"/>
253
+                    <s:enumeration value="Energy.Active.Import.Interval"/>
254
+                    <s:enumeration value="Energy.Reactive.Export.Interval"/>
255
+                    <s:enumeration value="Energy.Reactive.Import.Interval"/>
256
+                    <s:enumeration value="Power.Active.Export"/>
257
+                    <s:enumeration value="Power.Active.Import"/>
258
+                    <s:enumeration value="Power.Reactive.Export"/>
259
+                    <s:enumeration value="Power.Reactive.Import"/>
260
+                    <s:enumeration value="Current.Export"/>
261
+                    <s:enumeration value="Current.Import"/>
262
+                    <s:enumeration value="Voltage"/>
263
+                    <s:enumeration value="Temperature"/>
264
+                </s:restriction>
265
+            </s:simpleType>
266
+
267
+            <s:simpleType name="ValueFormat">
268
+                <s:restriction base="s:string">
269
+                    <s:enumeration value="Raw"/>
270
+                    <s:enumeration value="SignedData"/>
271
+                </s:restriction>
272
+            </s:simpleType>
273
+
274
+            <s:simpleType name="UnitOfMeasure">
275
+                <s:restriction base="s:string">
276
+                    <s:enumeration value="Wh"/>
277
+                    <s:enumeration value="kWh"/>
278
+                    <s:enumeration value="varh"/>
279
+                    <s:enumeration value="kvarh"/>
280
+                    <s:enumeration value="W"/>
281
+                    <s:enumeration value="kW"/>
282
+                    <s:enumeration value="var"/>
283
+                    <s:enumeration value="kvar"/>
284
+                    <s:enumeration value="Amp"/>
285
+                    <s:enumeration value="Volt"/>
286
+                    <s:enumeration value="Celsius"/>
287
+                </s:restriction>
288
+            </s:simpleType>
289
+
290
+            <s:simpleType name="Location">
291
+                <s:restriction base="s:string">
292
+                    <s:enumeration value="Inlet"/>
293
+                    <s:enumeration value="Outlet"/>
294
+                    <s:enumeration value="Body"/>
295
+                </s:restriction>
296
+            </s:simpleType>
297
+
298
+            <s:complexType name="MeterValue">
299
+                <s:annotation>
300
+                    <s:documentation>Defines single value of the meter-value-value</s:documentation>
301
+                </s:annotation>
302
+                <s:sequence>
303
+                    <s:element name="timestamp" type="s:dateTime"/>
304
+                    <s:element name="value" minOccurs="1" maxOccurs="unbounded">
305
+                        <s:complexType>
306
+                            <s:simpleContent>
307
+                                <s:extension base="s:string">
308
+                                    <s:attribute name="context" type="tns:ReadingContext" use="optional"/> <!-- default="Sample.Periodic" -->
309
+                                    <s:attribute name="format" type="tns:ValueFormat" use="optional"/> <!-- default="Raw" -->
310
+                                    <s:attribute name="measurand" type="tns:Measurand" use="optional"/> <!-- default="Energy.Active.Import.Register" -->
311
+                                    <s:attribute name="location" type="tns:Location" use="optional"/> <!-- default="Outlet" -->
312
+                                    <s:attribute name="unit" type="tns:UnitOfMeasure" use="optional"/> <!-- default="Wh" -->
313
+                                </s:extension>
314
+                            </s:simpleContent>
315
+                        </s:complexType>
316
+                    </s:element>
317
+                </s:sequence>
318
+            </s:complexType>
319
+
320
+            <s:complexType name="MeterValuesRequest">
321
+                <s:annotation>
322
+                    <s:documentation>Defines the MeterValues.req PDU</s:documentation>
323
+                </s:annotation>
324
+                <s:sequence>
325
+                    <s:element name="connectorId" type="s:int" minOccurs="1" maxOccurs="1"/>
326
+                    <s:element name="transactionId" type="s:int" minOccurs="0" maxOccurs="1"/>
327
+                    <s:element name="values" type="tns:MeterValue" minOccurs="0" maxOccurs="unbounded"/>
328
+                </s:sequence>
329
+            </s:complexType>
330
+
331
+            <s:complexType name="MeterValuesResponse">
332
+                <s:annotation>
333
+                    <s:documentation>Defines the MeterValues.conf PDU</s:documentation>
334
+                </s:annotation>
335
+            </s:complexType>
336
+
337
+            <s:complexType name="BootNotificationRequest">
338
+                <s:annotation>
339
+                    <s:documentation>Defines the BootNotification.req PDU</s:documentation>
340
+                </s:annotation>
341
+                <s:sequence>
342
+                    <s:element name="chargePointVendor" type="tns:ChargePointVendor" minOccurs="1" maxOccurs="1"/>
343
+                    <s:element name="chargePointModel" type="tns:ChargePointModel" minOccurs="1" maxOccurs="1"/>
344
+                    <s:element name="chargePointSerialNumber" type="tns:ChargePointSerialNumber" minOccurs="0" maxOccurs="1"/>
345
+                    <s:element name="chargeBoxSerialNumber" type="tns:ChargeBoxSerialNumber" minOccurs="0" maxOccurs="1"/>
346
+                    <s:element name="firmwareVersion" type="tns:FirmwareVersion" minOccurs="0" maxOccurs="1"/>
347
+                    <s:element name="iccid" type="tns:IccidString" minOccurs="0" maxOccurs="1"/>
348
+                    <s:element name="imsi" type="tns:ImsiString" minOccurs="0" maxOccurs="1"/>
349
+                    <s:element name="meterType" type="tns:MeterType" minOccurs="0" maxOccurs="1"/>
350
+                    <s:element name="meterSerialNumber" type="tns:MeterSerialNumber" minOccurs="0" maxOccurs="1"/>
351
+                </s:sequence>
352
+            </s:complexType>
353
+
354
+            <s:simpleType name="RegistrationStatus">
355
+                <s:annotation>
356
+                    <s:documentation>Defines the registration-status-value</s:documentation>
357
+                </s:annotation>
358
+                <s:restriction base="s:string">
359
+                    <s:enumeration value="Accepted"/>
360
+                    <s:enumeration value="Rejected"/>
361
+                </s:restriction>
362
+            </s:simpleType>
363
+
364
+            <s:complexType name="BootNotificationResponse">
365
+                <s:annotation>
366
+                    <s:documentation>Defines the BootNotification.conf PDU</s:documentation>
367
+                </s:annotation>
368
+                <s:sequence>
369
+                    <s:element name="status" type="tns:RegistrationStatus" minOccurs="1" maxOccurs="1"/>
370
+                    <s:element name="currentTime" type="s:dateTime" minOccurs="1" maxOccurs="1"/>
371
+                    <s:element name="heartbeatInterval" type="s:int" minOccurs="1" maxOccurs="1"/>
372
+                </s:sequence>
373
+            </s:complexType>
374
+
375
+            <s:simpleType name="ChargePointErrorCode">
376
+                <s:annotation>
377
+                    <s:documentation>Defines the charge-point-error-value</s:documentation>
378
+                </s:annotation>
379
+                <s:restriction base="s:string">
380
+                    <s:enumeration value="ConnectorLockFailure"/>
381
+                    <s:enumeration value="HighTemperature"/>
382
+                    <s:enumeration value="Mode3Error"/>
383
+                    <s:enumeration value="NoError"/>
384
+                    <s:enumeration value="PowerMeterFailure"/>
385
+                    <s:enumeration value="PowerSwitchFailure"/>
386
+                    <s:enumeration value="ReaderFailure"/>
387
+                    <s:enumeration value="ResetFailure"/>
388
+                    <s:enumeration value="GroundFailure"/>
389
+                    <s:enumeration value="OverCurrentFailure"/>
390
+                    <s:enumeration value="UnderVoltage"/>
391
+                    <s:enumeration value="WeakSignal"/>
392
+                    <s:enumeration value="OtherError"/>
393
+                </s:restriction>
394
+            </s:simpleType>
395
+
396
+            <s:simpleType name="ChargePointStatus">
397
+                <s:annotation>
398
+                    <s:documentation>Defines the charge-point-status-value</s:documentation>
399
+                </s:annotation>
400
+                <s:restriction base="s:string">
401
+                    <s:enumeration value="Available"/>
402
+                    <s:enumeration value="Occupied"/>
403
+                    <s:enumeration value="Faulted"/>
404
+                    <s:enumeration value="Unavailable"/>
405
+                    <s:enumeration value="Reserved"/>
406
+                </s:restriction>
407
+            </s:simpleType>
408
+
409
+            <s:complexType name="StatusNotificationRequest">
410
+                <s:annotation>
411
+                    <s:documentation>Defines the StatusNotification.req PDU</s:documentation>
412
+                </s:annotation>
413
+                <s:sequence>
414
+                    <s:element name="connectorId" type="s:int" minOccurs="1" maxOccurs="1"/>
415
+                    <s:element name="status" type="tns:ChargePointStatus" minOccurs="1" maxOccurs="1"/>
416
+                    <s:element name="errorCode" type="tns:ChargePointErrorCode" minOccurs="1" maxOccurs="1"/>
417
+                    <s:element name="info" type="s:string" minOccurs="0" maxOccurs="1"/>
418
+                    <s:element name="timestamp" type="s:dateTime" minOccurs="0" maxOccurs="1"/>
419
+                    <s:element name="vendorId" type="s:string" minOccurs="0" maxOccurs="1"/>
420
+                    <s:element name="vendorErrorCode" type="s:string" minOccurs="0" maxOccurs="1"/>
421
+                </s:sequence>
422
+            </s:complexType>
423
+
424
+            <s:complexType name="StatusNotificationResponse">
425
+                <s:annotation>
426
+                    <s:documentation>Defines the StatusNotification.conf PDU</s:documentation>
427
+                </s:annotation>
428
+            </s:complexType>
429
+
430
+            <s:simpleType name="FirmwareStatus">
431
+                <s:annotation>
432
+                    <s:documentation>Defines the firmware-status-value</s:documentation>
433
+                </s:annotation>
434
+                <s:restriction base="s:string">
435
+                    <s:enumeration value="Downloaded"/>
436
+                    <s:enumeration value="DownloadFailed"/>
437
+                    <s:enumeration value="InstallationFailed"/>
438
+                    <s:enumeration value="Installed"/>
439
+                </s:restriction>
440
+            </s:simpleType>
441
+
442
+            <s:complexType name="FirmwareStatusNotificationRequest">
443
+                <s:annotation>
444
+                    <s:documentation>Defines the FirmwareStatusNotification.req PDU</s:documentation>
445
+                </s:annotation>
446
+                <s:sequence>
447
+                    <s:element name="status" type="tns:FirmwareStatus" minOccurs="1" maxOccurs="1"/>
448
+                </s:sequence>
449
+            </s:complexType>
450
+
451
+            <s:complexType name="FirmwareStatusNotificationResponse">
452
+                <s:annotation>
453
+                    <s:documentation>Defines the FirmwareStatusNotification.conf PDU</s:documentation>
454
+                </s:annotation>
455
+            </s:complexType>
456
+
457
+            <s:simpleType name="DiagnosticsStatus">
458
+                <s:annotation>
459
+                    <s:documentation>Defines the diagnostics-status-value</s:documentation>
460
+                </s:annotation>
461
+                <s:restriction base="s:string">
462
+                    <s:enumeration value="Uploaded"/>
463
+                    <s:enumeration value="UploadFailed"/>
464
+                </s:restriction>
465
+            </s:simpleType>
466
+
467
+            <s:complexType name="DiagnosticsStatusNotificationRequest">
468
+                <s:annotation>
469
+                    <s:documentation>Defines the DiagnosticsStatusNotification.req PDU</s:documentation>
470
+                </s:annotation>
471
+                <s:sequence>
472
+                    <s:element name="status" type="tns:DiagnosticsStatus" minOccurs="1" maxOccurs="1"/>
473
+                </s:sequence>
474
+            </s:complexType>
475
+
476
+            <s:complexType name="DiagnosticsStatusNotificationResponse">
477
+                <s:annotation>
478
+                    <s:documentation>Defines the DiagnosticsStatusNotification.conf PDU</s:documentation>
479
+                </s:annotation>
480
+            </s:complexType>
481
+
482
+            <s:complexType name="DataTransferRequest">
483
+                <s:annotation>
484
+                    <s:documentation>Defines the DataTransfer.req PDU</s:documentation>
485
+                </s:annotation>
486
+                <s:sequence>
487
+                    <s:element name="vendorId" type="s:string" minOccurs="1" maxOccurs="1"/>
488
+                    <s:element name="messageId" type="s:string" minOccurs="0" maxOccurs="1"/>
489
+                    <s:element name="data" type="s:string" minOccurs="0" maxOccurs="1"/>
490
+                </s:sequence>
491
+            </s:complexType>
492
+
493
+            <s:simpleType name="DataTransferStatus">
494
+                <s:annotation>
495
+                    <s:documentation>Defines the status returned in DataTransfer.conf</s:documentation>
496
+                </s:annotation>
497
+                <s:restriction base="s:string">
498
+                    <s:enumeration value="Accepted"/>
499
+                    <s:enumeration value="Rejected"/>
500
+                    <s:enumeration value="UnknownMessageId"/>
501
+                    <s:enumeration value="UnknownVendorId"/>
502
+                </s:restriction>
503
+            </s:simpleType>
504
+
505
+            <s:complexType name="DataTransferResponse">
506
+                <s:annotation>
507
+                    <s:documentation>Defines the DataTransfer.conf PDU</s:documentation>
508
+                </s:annotation>
509
+                <s:sequence>
510
+                    <s:element name="status" type="tns:DataTransferStatus" minOccurs="1" maxOccurs="1"/>
511
+                    <s:element name="data" type="s:string" minOccurs="0" maxOccurs="1"/>
512
+                </s:sequence>
513
+            </s:complexType>
514
+
515
+            <s:element name="chargeBoxIdentity" type="s:string"/>
516
+            <s:element name="authorizeRequest" type="tns:AuthorizeRequest"/>
517
+            <s:element name="authorizeResponse" type="tns:AuthorizeResponse"/>
518
+            <s:element name="startTransactionRequest" type="tns:StartTransactionRequest"/>
519
+            <s:element name="startTransactionResponse" type="tns:StartTransactionResponse"/>
520
+            <s:element name="stopTransactionRequest" type="tns:StopTransactionRequest"/>
521
+            <s:element name="stopTransactionResponse" type="tns:StopTransactionResponse"/>
522
+            <s:element name="heartbeatRequest" type="tns:HeartbeatRequest"/>
523
+            <s:element name="heartbeatResponse" type="tns:HeartbeatResponse"/>
524
+            <s:element name="meterValuesRequest" type="tns:MeterValuesRequest"/>
525
+            <s:element name="meterValuesResponse" type="tns:MeterValuesResponse"/>
526
+            <s:element name="bootNotificationRequest" type="tns:BootNotificationRequest"/>
527
+            <s:element name="bootNotificationResponse" type="tns:BootNotificationResponse"/>
528
+            <s:element name="statusNotificationRequest" type="tns:StatusNotificationRequest"/>
529
+            <s:element name="statusNotificationResponse" type="tns:StatusNotificationResponse"/>
530
+            <s:element name="firmwareStatusNotificationRequest" type="tns:FirmwareStatusNotificationRequest"/>
531
+            <s:element name="firmwareStatusNotificationResponse" type="tns:FirmwareStatusNotificationResponse"/>
532
+            <s:element name="diagnosticsStatusNotificationRequest" type="tns:DiagnosticsStatusNotificationRequest"/>
533
+            <s:element name="diagnosticsStatusNotificationResponse" type="tns:DiagnosticsStatusNotificationResponse"/>
534
+            <s:element name="dataTransferRequest" type="tns:DataTransferRequest"/>
535
+            <s:element name="dataTransferResponse" type="tns:DataTransferResponse"/>
536
+        </s:schema>
537
+    </wsdl:types>
538
+    <wsdl:message name="Header">
539
+        <wsdl:part name="ChargeBoxIdentity" element="tns:chargeBoxIdentity"/>
540
+    </wsdl:message>
541
+    <wsdl:message name="AuthorizeInput">
542
+        <wsdl:part name="parameters" element="tns:authorizeRequest"/>
543
+    </wsdl:message>
544
+    <wsdl:message name="AuthorizeOutput">
545
+        <wsdl:part name="parameters" element="tns:authorizeResponse"/>
546
+    </wsdl:message>
547
+    <wsdl:message name="StartTransactionInput">
548
+        <wsdl:part name="parameters" element="tns:startTransactionRequest"/>
549
+    </wsdl:message>
550
+    <wsdl:message name="StartTransactionOutput">
551
+        <wsdl:part name="parameters" element="tns:startTransactionResponse"/>
552
+    </wsdl:message>
553
+    <wsdl:message name="StopTransactionInput">
554
+        <wsdl:part name="parameters" element="tns:stopTransactionRequest"/>
555
+    </wsdl:message>
556
+    <wsdl:message name="StopTransactionOutput">
557
+        <wsdl:part name="parameters" element="tns:stopTransactionResponse"/>
558
+    </wsdl:message>
559
+    <wsdl:message name="HeartbeatInput">
560
+        <wsdl:part name="parameters" element="tns:heartbeatRequest"/>
561
+    </wsdl:message>
562
+    <wsdl:message name="HeartbeatOutput">
563
+        <wsdl:part name="parameters" element="tns:heartbeatResponse"/>
564
+    </wsdl:message>
565
+    <wsdl:message name="MeterValuesInput">
566
+        <wsdl:part name="parameters" element="tns:meterValuesRequest"/>
567
+    </wsdl:message>
568
+    <wsdl:message name="MeterValuesOutput">
569
+        <wsdl:part name="parameters" element="tns:meterValuesResponse"/>
570
+    </wsdl:message>
571
+    <wsdl:message name="BootNotificationInput">
572
+        <wsdl:part name="parameters" element="tns:bootNotificationRequest"/>
573
+    </wsdl:message>
574
+    <wsdl:message name="BootNotificationOutput">
575
+        <wsdl:part name="parameters" element="tns:bootNotificationResponse"/>
576
+    </wsdl:message>
577
+    <wsdl:message name="StatusNotificationInput">
578
+        <wsdl:part name="parameters" element="tns:statusNotificationRequest"/>
579
+    </wsdl:message>
580
+    <wsdl:message name="StatusNotificationOutput">
581
+        <wsdl:part name="parameters" element="tns:statusNotificationResponse"/>
582
+    </wsdl:message>
583
+    <wsdl:message name="FirmwareStatusNotificationInput">
584
+        <wsdl:part name="parameters" element="tns:firmwareStatusNotificationRequest"/>
585
+    </wsdl:message>
586
+    <wsdl:message name="FirmwareStatusNotificationOutput">
587
+        <wsdl:part name="parameters" element="tns:firmwareStatusNotificationResponse"/>
588
+    </wsdl:message>
589
+    <wsdl:message name="DiagnosticsStatusNotificationInput">
590
+        <wsdl:part name="parameters" element="tns:diagnosticsStatusNotificationRequest"/>
591
+    </wsdl:message>
592
+    <wsdl:message name="DiagnosticsStatusNotificationOutput">
593
+        <wsdl:part name="parameters" element="tns:diagnosticsStatusNotificationResponse"/>
594
+    </wsdl:message>
595
+    <wsdl:message name="DataTransferInput">
596
+        <wsdl:part name="parameters" element="tns:dataTransferRequest"/>
597
+    </wsdl:message>
598
+    <wsdl:message name="DataTransferOutput">
599
+        <wsdl:part name="parameters" element="tns:dataTransferResponse"/>
600
+    </wsdl:message>
601
+    <wsdl:portType name="CentralSystemService">
602
+        <wsdl:operation name="Authorize">
603
+            <wsdl:input message="tns:AuthorizeInput" wsaw:Action="/Authorize"/>
604
+            <wsdl:output message="tns:AuthorizeOutput" wsaw:Action="/AuthorizeResponse"/>
605
+        </wsdl:operation>
606
+        <wsdl:operation name="StartTransaction">
607
+            <wsdl:input message="tns:StartTransactionInput" wsaw:Action="/StartTransaction"/>
608
+            <wsdl:output message="tns:StartTransactionOutput" wsaw:Action="/StartTransactionResponse"/>
609
+        </wsdl:operation>
610
+        <wsdl:operation name="StopTransaction">
611
+            <wsdl:input message="tns:StopTransactionInput" wsaw:Action="/StopTransaction"/>
612
+            <wsdl:output message="tns:StopTransactionOutput" wsaw:Action="/StopTransactionResponse"/>
613
+        </wsdl:operation>
614
+        <wsdl:operation name="Heartbeat">
615
+            <wsdl:input message="tns:HeartbeatInput" wsaw:Action="/Heartbeat"/>
616
+            <wsdl:output message="tns:HeartbeatOutput" wsaw:Action="/HeartbeatResponse"/>
617
+        </wsdl:operation>
618
+        <wsdl:operation name="MeterValues">
619
+            <wsdl:input message="tns:MeterValuesInput" wsaw:Action="/MeterValues"/>
620
+            <wsdl:output message="tns:MeterValuesOutput" wsaw:Action="/MeterValuesResponse"/>
621
+        </wsdl:operation>
622
+        <wsdl:operation name="BootNotification">
623
+            <wsdl:input message="tns:BootNotificationInput" wsaw:Action="/BootNotification"/>
624
+            <wsdl:output message="tns:BootNotificationOutput" wsaw:Action="/BootNotificationResponse"/>
625
+        </wsdl:operation>
626
+        <wsdl:operation name="StatusNotification">
627
+            <wsdl:input message="tns:StatusNotificationInput" wsaw:Action="/StatusNotification"/>
628
+            <wsdl:output message="tns:StatusNotificationOutput" wsaw:Action="/StatusNotificationResponse"/>
629
+        </wsdl:operation>
630
+        <wsdl:operation name="FirmwareStatusNotification">
631
+            <wsdl:input message="tns:FirmwareStatusNotificationInput" wsaw:Action="/FirmwareStatusNotification"/>
632
+            <wsdl:output message="tns:FirmwareStatusNotificationOutput" wsaw:Action="/FirmwareStatusNotificationResponse"/>
633
+        </wsdl:operation>
634
+        <wsdl:operation name="DiagnosticsStatusNotification">
635
+            <wsdl:input message="tns:DiagnosticsStatusNotificationInput" wsaw:Action="/DiagnosticsStatusNotification"/>
636
+            <wsdl:output message="tns:DiagnosticsStatusNotificationOutput" wsaw:Action="/DiagnosticsStatusNotificationResponse"/>
637
+        </wsdl:operation>
638
+        <wsdl:operation name="DataTransfer">
639
+            <wsdl:input message="tns:DataTransferInput" wsaw:Action="/DataTransfer"/>
640
+            <wsdl:output message="tns:DataTransferOutput" wsaw:Action="/DataTransferResponse"/>
641
+        </wsdl:operation>
642
+    </wsdl:portType>
643
+    <wsdl:binding name="CentralSystemServiceSoap" type="tns:CentralSystemService">
644
+        <soap12:binding transport="http://schemas.xmlsoap.org/soap/http"/>
645
+        <wsp:PolicyReference URI="#ServicePolicy"/>
646
+        <wsdl:operation name="Authorize">
647
+            <soap12:operation soapAction="/Authorize" style="document"/>
648
+            <wsdl:input>
649
+                <soap12:header message="tns:Header" part="ChargeBoxIdentity" use="literal"/>
650
+                <soap12:body use="literal"/>
651
+            </wsdl:input>
652
+            <wsdl:output>
653
+                <soap12:body use="literal"/>
654
+            </wsdl:output>
655
+        </wsdl:operation>
656
+        <wsdl:operation name="StartTransaction">
657
+            <soap12:operation soapAction="/StartTransaction" style="document"/>
658
+            <wsdl:input>
659
+                <soap12:header message="tns:Header" part="ChargeBoxIdentity" use="literal"/>
660
+                <soap12:body use="literal"/>
661
+            </wsdl:input>
662
+            <wsdl:output>
663
+                <soap12:body use="literal"/>
664
+            </wsdl:output>
665
+        </wsdl:operation>
666
+        <wsdl:operation name="StopTransaction">
667
+            <soap12:operation soapAction="/StopTransaction" style="document"/>
668
+            <wsdl:input>
669
+                <soap12:header message="tns:Header" part="ChargeBoxIdentity" use="literal"/>
670
+                <soap12:body use="literal"/>
671
+            </wsdl:input>
672
+            <wsdl:output>
673
+                <soap12:body use="literal"/>
674
+            </wsdl:output>
675
+        </wsdl:operation>
676
+        <wsdl:operation name="Heartbeat">
677
+            <soap12:operation soapAction="/Heartbeat" style="document"/>
678
+            <wsdl:input>
679
+                <soap12:header message="tns:Header" part="ChargeBoxIdentity" use="literal"/>
680
+                <soap12:body use="literal"/>
681
+            </wsdl:input>
682
+            <wsdl:output>
683
+                <soap12:body use="literal"/>
684
+            </wsdl:output>
685
+        </wsdl:operation>
686
+        <wsdl:operation name="MeterValues">
687
+            <soap12:operation soapAction="/MeterValues" style="document"/>
688
+            <wsdl:input>
689
+                <soap12:header message="tns:Header" part="ChargeBoxIdentity" use="literal"/>
690
+                <soap12:body use="literal"/>
691
+            </wsdl:input>
692
+            <wsdl:output>
693
+                <soap12:body use="literal"/>
694
+            </wsdl:output>
695
+        </wsdl:operation>
696
+        <wsdl:operation name="BootNotification">
697
+            <soap12:operation soapAction="/BootNotification" style="document"/>
698
+            <wsdl:input>
699
+                <soap12:header message="tns:Header" part="ChargeBoxIdentity" use="literal"/>
700
+                <soap12:body use="literal"/>
701
+            </wsdl:input>
702
+            <wsdl:output>
703
+                <soap12:body use="literal"/>
704
+            </wsdl:output>
705
+        </wsdl:operation>
706
+        <wsdl:operation name="StatusNotification">
707
+            <soap12:operation soapAction="/StatusNotification" style="document"/>
708
+            <wsdl:input>
709
+                <soap12:header message="tns:Header" part="ChargeBoxIdentity" use="literal"/>
710
+                <soap12:body use="literal"/>
711
+            </wsdl:input>
712
+            <wsdl:output>
713
+                <soap12:body use="literal"/>
714
+            </wsdl:output>
715
+        </wsdl:operation>
716
+        <wsdl:operation name="FirmwareStatusNotification">
717
+            <soap12:operation soapAction="/FirmwareStatusNotification" style="document"/>
718
+            <wsdl:input>
719
+                <soap12:header message="tns:Header" part="ChargeBoxIdentity" use="literal"/>
720
+                <soap12:body use="literal"/>
721
+            </wsdl:input>
722
+            <wsdl:output>
723
+                <soap12:body use="literal"/>
724
+            </wsdl:output>
725
+        </wsdl:operation>
726
+        <wsdl:operation name="DiagnosticsStatusNotification">
727
+            <soap12:operation soapAction="/DiagnosticsStatusNotification" style="document"/>
728
+            <wsdl:input>
729
+                <soap12:header message="tns:Header" part="ChargeBoxIdentity" use="literal"/>
730
+                <soap12:body use="literal"/>
731
+            </wsdl:input>
732
+            <wsdl:output>
733
+                <soap12:body use="literal"/>
734
+            </wsdl:output>
735
+        </wsdl:operation>
736
+        <wsdl:operation name="DataTransfer">
737
+            <soap12:operation soapAction="/DataTransfer" style="document"/>
738
+            <wsdl:input>
739
+                <soap12:header message="tns:Header" part="ChargeBoxIdentity" use="literal"/>
740
+                <soap12:body use="literal"/>
741
+            </wsdl:input>
742
+            <wsdl:output>
743
+                <soap12:body use="literal"/>
744
+            </wsdl:output>
745
+        </wsdl:operation>
746
+    </wsdl:binding>
747
+
748
+    <!-- The OCPP Central System Service -->
749
+    <wsdl:service name="CentralSystemService">
750
+        <wsdl:documentation>The Central System Service for the Open Charge Point Protocol</wsdl:documentation>
751
+        <wsdl:port name="CentralSystemServiceSoap12" binding="tns:CentralSystemServiceSoap">
752
+            <!-- <soap12:address location="http://localhost/Ocpp/CentralSystemService/"/> -->
753
+            <soap12:address location="http://127.0.0.1:9220/Ocpp/CentralSystemService"/>
754
+        </wsdl:port>
755
+    </wsdl:service>
756
+
757
+</wsdl:definitions>

+ 904 - 0
charger-simulator/src/soap/wsdl/15/ocpp_chargepointservice_1.5_final.wsdl

@@ -0,0 +1,904 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<!--
3
+OCPP Charge Point service description
4
+Version 1.5 draft for approval (1.5 RC4)
5
+
6
+Copyright e-laad.nl. Free to use without modifications.
7
+
8
+See: http://ev-services.net/ocpp/
9
+-->
10
+<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
11
+                  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
12
+                  xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
13
+                  xmlns:s="http://www.w3.org/2001/XMLSchema"
14
+                  xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
15
+                  xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
16
+                  xmlns:tns="urn://Ocpp/Cp/2012/06/"
17
+                  targetNamespace="urn://Ocpp/Cp/2012/06/"
18
+                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
19
+                  xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
20
+                  xmlns:wsa="http://www.w3.org/2005/08/addressing"
21
+                  xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
22
+                  xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
23
+
24
+    <wsp:Policy wsu:Id="ServicePolicy">
25
+        <wsp:ExactlyOne>
26
+            <wsp:All>
27
+                <wsaw:UsingAddressing />
28
+            </wsp:All>
29
+        </wsp:ExactlyOne>
30
+    </wsp:Policy>
31
+
32
+    <wsdl:types>
33
+        <s:schema elementFormDefault="qualified" targetNamespace="urn://Ocpp/Cp/2012/06/">
34
+
35
+            <!-- Begin of types shared with CentralSystemService -->
36
+            <s:simpleType name="IdToken">
37
+                <s:annotation>
38
+                    <s:documentation>Type of string defining identification token, e.g. RFID or credit card number. To be treated as case insensitive.</s:documentation>
39
+                </s:annotation>
40
+                <s:restriction base="s:string">
41
+                    <s:maxLength value="20"/>
42
+                </s:restriction>
43
+            </s:simpleType>
44
+
45
+            <s:simpleType name="AuthorizationStatus">
46
+                <s:annotation>
47
+                    <s:documentation>Defines the authorization-status-value</s:documentation>
48
+                </s:annotation>
49
+                <s:restriction base="s:string">
50
+                    <s:enumeration value="Accepted"/>
51
+                    <s:enumeration value="Blocked"/>
52
+                    <s:enumeration value="Expired"/>
53
+                    <s:enumeration value="Invalid"/>
54
+                    <s:enumeration value="ConcurrentTx"/>
55
+                </s:restriction>
56
+            </s:simpleType>
57
+
58
+            <s:complexType name="IdTagInfo">
59
+                <s:sequence>
60
+                    <s:element name="status" type="tns:AuthorizationStatus" minOccurs="1" maxOccurs="1"/>
61
+                    <s:element name="expiryDate" type="s:dateTime" minOccurs="0" maxOccurs="1"/>
62
+                    <s:element name="parentIdTag" type="tns:IdToken" minOccurs="0" maxOccurs="1"/>
63
+                </s:sequence>
64
+            </s:complexType>
65
+            <!-- End of types shared with CentralSystemService -->
66
+
67
+            <s:simpleType name="UnlockStatus">
68
+                <s:annotation>
69
+                    <s:documentation>Defines the unlock-status-value</s:documentation>
70
+                </s:annotation>
71
+                <s:restriction base="s:string">
72
+                    <s:enumeration value="Accepted" />
73
+                    <s:enumeration value="Rejected" />
74
+                </s:restriction>
75
+            </s:simpleType>
76
+
77
+            <s:complexType name="UnlockConnectorRequest">
78
+                <s:annotation>
79
+                    <s:documentation>Defines the UnlockConnector.req PDU</s:documentation>
80
+                </s:annotation>
81
+                <s:sequence>
82
+                    <s:element name="connectorId" type="s:int" minOccurs="1" maxOccurs="1" />
83
+                </s:sequence>
84
+            </s:complexType>
85
+
86
+            <s:complexType name="UnlockConnectorResponse">
87
+                <s:annotation>
88
+                    <s:documentation>Defines the UnlockConnector.conf PDU</s:documentation>
89
+                </s:annotation>
90
+                <s:sequence>
91
+                    <s:element name="status" type="tns:UnlockStatus" minOccurs="1" maxOccurs="1" />
92
+                </s:sequence>
93
+            </s:complexType>
94
+
95
+            <s:simpleType name="ResetType">
96
+                <s:annotation>
97
+                    <s:documentation>Defines the reset-type-value</s:documentation>
98
+                </s:annotation>
99
+                <s:restriction base="s:string">
100
+                    <s:enumeration value="Hard" />
101
+                    <s:enumeration value="Soft" />
102
+                </s:restriction>
103
+            </s:simpleType>
104
+
105
+            <s:complexType name="ResetRequest">
106
+                <s:annotation>
107
+                    <s:documentation>Defines the Reset.req PDU</s:documentation>
108
+                </s:annotation>
109
+                <s:sequence>
110
+                    <s:element name="type" type="tns:ResetType" minOccurs="1" maxOccurs="1" />
111
+                </s:sequence>
112
+            </s:complexType>
113
+
114
+            <s:simpleType name="ResetStatus">
115
+                <s:annotation>
116
+                    <s:documentation>Defines the reset-status-value</s:documentation>
117
+                </s:annotation>
118
+                <s:restriction base="s:string">
119
+                    <s:enumeration value="Accepted" />
120
+                    <s:enumeration value="Rejected" />
121
+                </s:restriction>
122
+            </s:simpleType>
123
+
124
+            <s:complexType name="ResetResponse">
125
+                <s:sequence>
126
+                    <s:element name="status" type="tns:ResetStatus" minOccurs="1" maxOccurs="1" />
127
+                </s:sequence>
128
+            </s:complexType>
129
+
130
+            <s:simpleType name="AvailabilityType">
131
+                <s:annotation>
132
+                    <s:documentation>Defines the availability-type-value</s:documentation>
133
+                </s:annotation>
134
+                <s:restriction base="s:string">
135
+                    <s:enumeration value="Inoperative" />
136
+                    <s:enumeration value="Operative" />
137
+                </s:restriction>
138
+            </s:simpleType>
139
+
140
+            <s:complexType name="ChangeAvailabilityRequest">
141
+                <s:annotation>
142
+                    <s:documentation>Defines the ChangeAvailability.req PDU</s:documentation>
143
+                </s:annotation>
144
+                <s:sequence>
145
+                    <s:element name="connectorId" type="s:int" minOccurs="1" maxOccurs="1" />
146
+                    <s:element name="type" type="tns:AvailabilityType" minOccurs="1" maxOccurs="1" />
147
+                </s:sequence>
148
+            </s:complexType>
149
+
150
+            <s:simpleType name="AvailabilityStatus">
151
+                <s:annotation>
152
+                    <s:documentation>Defines the availability-status-value</s:documentation>
153
+                </s:annotation>
154
+                <s:restriction base="s:string">
155
+                    <s:enumeration value="Accepted" />
156
+                    <s:enumeration value="Rejected" />
157
+                    <s:enumeration value="Scheduled" />
158
+                </s:restriction>
159
+            </s:simpleType>
160
+
161
+            <s:complexType name="ChangeAvailabilityResponse">
162
+                <s:annotation>
163
+                    <s:documentation>Defines the ChangeAvailability.conf PDU</s:documentation>
164
+                </s:annotation>
165
+                <s:sequence>
166
+                    <s:element name="status" type="tns:AvailabilityStatus" minOccurs="1" maxOccurs="1" />
167
+                </s:sequence>
168
+            </s:complexType>
169
+
170
+            <s:complexType name="GetDiagnosticsRequest">
171
+                <s:annotation>
172
+                    <s:documentation>Defines the GetDiagnostics.req PDU</s:documentation>
173
+                </s:annotation>
174
+                <s:sequence>
175
+                    <s:element name="location" type="s:anyURI" minOccurs="1" maxOccurs="1" />
176
+                    <s:element name="startTime" type="s:dateTime" minOccurs="0" maxOccurs="1" />
177
+                    <s:element name="stopTime" type="s:dateTime" minOccurs="0" maxOccurs="1" />
178
+                    <s:element name="retries" type="s:int" minOccurs="0" maxOccurs="1" />
179
+                    <s:element name="retryInterval" type="s:int" minOccurs="0" maxOccurs="1" />
180
+                </s:sequence>
181
+            </s:complexType>
182
+
183
+            <s:complexType name="GetDiagnosticsResponse">
184
+                <s:annotation>
185
+                    <s:documentation>Defines the GetDiagnostics.conf PDU</s:documentation>
186
+                </s:annotation>
187
+                <s:sequence>
188
+                    <s:element name="fileName" type="s:string" minOccurs="0" maxOccurs="1" />
189
+                </s:sequence>
190
+            </s:complexType>
191
+
192
+            <s:complexType name="ClearCacheRequest">
193
+                <s:annotation>
194
+                    <s:documentation>Defines the ClearCache.req PDU</s:documentation>
195
+                </s:annotation>
196
+            </s:complexType>
197
+
198
+            <s:simpleType name="ClearCacheStatus">
199
+                <s:annotation>
200
+                    <s:documentation>Defines the clear-cache-status-value</s:documentation>
201
+                </s:annotation>
202
+                <s:restriction base="s:string">
203
+                    <s:enumeration value="Accepted" />
204
+                    <s:enumeration value="Rejected" />
205
+                </s:restriction>
206
+            </s:simpleType>
207
+
208
+            <s:complexType name="ClearCacheResponse">
209
+                <s:annotation>
210
+                    <s:documentation>Defines the ClearCache.conf PDU</s:documentation>
211
+                </s:annotation>
212
+                <s:sequence>
213
+                    <s:element name="status" type="tns:ClearCacheStatus" minOccurs="1" maxOccurs="1" />
214
+                </s:sequence>
215
+            </s:complexType>
216
+
217
+            <s:complexType name="UpdateFirmwareRequest">
218
+                <s:sequence>
219
+                    <s:element name="retrieveDate" type="s:dateTime" minOccurs="1" maxOccurs="1" />
220
+                    <s:element name="location" type="s:anyURI" minOccurs="1" maxOccurs="1" />
221
+                    <s:element name="retries" type="s:int" minOccurs="0" maxOccurs="1" />
222
+                    <s:element name="retryInterval" type="s:int" minOccurs="0" maxOccurs="1" />
223
+                </s:sequence>
224
+            </s:complexType>
225
+
226
+            <s:complexType name="UpdateFirmwareResponse">
227
+            </s:complexType>
228
+
229
+            <s:complexType name="ChangeConfigurationRequest">
230
+                <s:annotation>
231
+                    <s:documentation>Defines the ChangeConfiguration.req PDU</s:documentation>
232
+                </s:annotation>
233
+                <s:sequence>
234
+                    <s:element name="key" type="s:string" minOccurs="1" maxOccurs="1" />
235
+                    <s:element name="value" type="s:string" minOccurs="1" maxOccurs="1" />
236
+                </s:sequence>
237
+            </s:complexType>
238
+
239
+            <s:simpleType name="ConfigurationStatus">
240
+                <s:annotation>
241
+                    <s:documentation>Defines the configuration-status-value</s:documentation>
242
+                </s:annotation>
243
+                <s:restriction base="s:string">
244
+                    <s:enumeration value="Accepted" />
245
+                    <s:enumeration value="Rejected" />
246
+                    <s:enumeration value="NotSupported" />
247
+                </s:restriction>
248
+            </s:simpleType>
249
+
250
+            <s:complexType name="ChangeConfigurationResponse">
251
+                <s:annotation>
252
+                    <s:documentation>Defines the ChangeConfiguration.conf PDU</s:documentation>
253
+                </s:annotation>
254
+                <s:sequence>
255
+                    <s:element name="status" type="tns:ConfigurationStatus" minOccurs="1" maxOccurs="1" />
256
+                </s:sequence>
257
+            </s:complexType>
258
+
259
+            <s:complexType name="RemoteStartTransactionRequest">
260
+                <s:annotation>
261
+                    <s:documentation>Defines the RemoteStartTransaction.req PDU</s:documentation>
262
+                </s:annotation>
263
+                <s:sequence>
264
+                    <s:element name="idTag" type="tns:IdToken" minOccurs="1" maxOccurs="1" />
265
+                    <s:element name="connectorId" type="s:int" minOccurs="0" maxOccurs="1" />
266
+                </s:sequence>
267
+            </s:complexType>
268
+
269
+            <s:simpleType name="RemoteStartStopStatus">
270
+                <s:annotation>
271
+                    <s:documentation>Defines the remote-start-stop-status-value</s:documentation>
272
+                </s:annotation>
273
+                <s:restriction base="s:string">
274
+                    <s:enumeration value="Accepted" />
275
+                    <s:enumeration value="Rejected" />
276
+                </s:restriction>
277
+            </s:simpleType>
278
+
279
+            <s:complexType name="RemoteStartTransactionResponse">
280
+                <s:annotation>
281
+                    <s:documentation>Defines the RemoteStartTransaction.conf PDU</s:documentation>
282
+                </s:annotation>
283
+                <s:sequence>
284
+                    <s:element name="status" type="tns:RemoteStartStopStatus" minOccurs="1" maxOccurs="1" />
285
+                </s:sequence>
286
+            </s:complexType>
287
+
288
+            <s:complexType name="RemoteStopTransactionRequest">
289
+                <s:annotation>
290
+                    <s:documentation>Defines the RemoteStopTransaction.req PDU</s:documentation>
291
+                </s:annotation>
292
+                <s:sequence>
293
+                    <s:element name="transactionId" type="s:int" minOccurs="1" maxOccurs="1" />
294
+                </s:sequence>
295
+            </s:complexType>
296
+
297
+            <s:complexType name="RemoteStopTransactionResponse">
298
+                <s:annotation>
299
+                    <s:documentation>Defines the RemoteStopTransaction.conf PDU</s:documentation>
300
+                </s:annotation>
301
+                <s:sequence>
302
+                    <s:element name="status" type="tns:RemoteStartStopStatus" minOccurs="1" maxOccurs="1" />
303
+                </s:sequence>
304
+            </s:complexType>
305
+
306
+            <s:complexType name="CancelReservationRequest">
307
+                <s:annotation>
308
+                    <s:documentation>Defines the CancelReservation.req PDU</s:documentation>
309
+                </s:annotation>
310
+                <s:sequence>
311
+                    <s:element name="reservationId" type="s:int" minOccurs="1" maxOccurs="1" />
312
+                </s:sequence>
313
+            </s:complexType>
314
+
315
+            <s:simpleType name="CancelReservationStatus">
316
+                <s:restriction base="s:string">
317
+                    <s:enumeration value="Accepted"/>
318
+                    <s:enumeration value="Rejected"/>
319
+                </s:restriction>
320
+            </s:simpleType>
321
+
322
+            <s:complexType name="CancelReservationResponse">
323
+                <s:annotation>
324
+                    <s:documentation>Defines the CancelReservation.conf PDU</s:documentation>
325
+                </s:annotation>
326
+                <s:sequence>
327
+                    <s:element name="status" type="tns:CancelReservationStatus" minOccurs="1" maxOccurs="1" />
328
+                </s:sequence>
329
+            </s:complexType>
330
+
331
+            <s:complexType name="DataTransferRequest">
332
+                <s:annotation>
333
+                    <s:documentation>Defines the DataTransfer.req PDU</s:documentation>
334
+                </s:annotation>
335
+                <s:sequence>
336
+                    <s:element name="vendorId" type="s:string" minOccurs="1" maxOccurs="1" />
337
+                    <s:element name="messageId" type="s:string" minOccurs="0" maxOccurs="1" />
338
+                    <s:element name="data" type="s:string" minOccurs="0" maxOccurs="1" />
339
+                </s:sequence>
340
+            </s:complexType>
341
+
342
+            <s:simpleType name="DataTransferStatus">
343
+                <s:annotation>
344
+                    <s:documentation>Defines the status returned in DataTransfer.conf</s:documentation>
345
+                </s:annotation>
346
+                <s:restriction base="s:string">
347
+                    <s:enumeration value="Accepted" />
348
+                    <s:enumeration value="Rejected" />
349
+                    <s:enumeration value="UnknownMessageId" />
350
+                    <s:enumeration value="UnknownVendorId" />
351
+                </s:restriction>
352
+            </s:simpleType>
353
+
354
+            <s:complexType name="DataTransferResponse">
355
+                <s:annotation>
356
+                    <s:documentation>Defines the DataTransfer.conf PDU</s:documentation>
357
+                </s:annotation>
358
+                <s:sequence>
359
+                    <s:element name="status" type="tns:DataTransferStatus" minOccurs="1" maxOccurs="1" />
360
+                    <s:element name="data" type="s:string" minOccurs="0" maxOccurs="1" />
361
+                </s:sequence>
362
+            </s:complexType>
363
+
364
+            <s:complexType name="GetConfigurationRequest">
365
+                <s:annotation>
366
+                    <s:documentation>Defines the GetConfiguration.req PDU</s:documentation>
367
+                </s:annotation>
368
+                <s:sequence>
369
+                    <s:element name="key" type="s:string" minOccurs="0" maxOccurs="unbounded" />
370
+                </s:sequence>
371
+            </s:complexType>
372
+
373
+            <s:complexType name="KeyValue">
374
+                <s:annotation>
375
+                    <s:documentation>Key-Value pairs returned by GetConfiguration.conf PDU</s:documentation>
376
+                </s:annotation>
377
+                <s:sequence>
378
+                    <s:element name="key" type="s:string" minOccurs="1" maxOccurs="1" />
379
+                    <s:element name="readonly" type="s:boolean" minOccurs="1" maxOccurs="1" />
380
+                    <s:element name="value" type="s:string" minOccurs="0" maxOccurs="1" />
381
+                </s:sequence>
382
+            </s:complexType>
383
+
384
+            <s:complexType name="GetConfigurationResponse">
385
+                <s:annotation>
386
+                    <s:documentation>Defines the GetConfiguration.req PDU</s:documentation>
387
+                </s:annotation>
388
+                <s:sequence>
389
+                    <s:element name="configurationKey" type="tns:KeyValue" minOccurs="0" maxOccurs="unbounded" />
390
+                    <s:element name="unknownKey" type="s:string" minOccurs="0" maxOccurs="unbounded" />
391
+                </s:sequence>
392
+            </s:complexType>
393
+
394
+            <s:complexType name="GetLocalListVersionRequest">
395
+                <s:annotation>
396
+                    <s:documentation>Defines the GetLocalListVersion.req PDU</s:documentation>
397
+                </s:annotation>
398
+            </s:complexType>
399
+
400
+            <s:complexType name="GetLocalListVersionResponse">
401
+                <s:annotation>
402
+                    <s:documentation>Defines the GetLocalListVersion.conf PDU</s:documentation>
403
+                </s:annotation>
404
+                <s:sequence>
405
+                    <s:element name="listVersion" type="s:int" minOccurs="1" maxOccurs="1" />
406
+                </s:sequence>
407
+            </s:complexType>
408
+
409
+            <s:complexType name="ReserveNowRequest">
410
+                <s:annotation>
411
+                    <s:documentation>Defines the ReserveNow.req PDU</s:documentation>
412
+                </s:annotation>
413
+                <s:sequence>
414
+                    <s:element name="connectorId" type="s:int" minOccurs="1" maxOccurs="1" />
415
+                    <s:element name="expiryDate" type="s:dateTime" minOccurs="1" maxOccurs="1" />
416
+                    <s:element name="idTag" type="tns:IdToken" minOccurs="1" maxOccurs="1" />
417
+                    <s:element name="parentIdTag" type="tns:IdToken" minOccurs="0" maxOccurs="1" />
418
+                    <s:element name="reservationId" type="s:int" minOccurs="1" maxOccurs="1" />
419
+                </s:sequence>
420
+            </s:complexType>
421
+
422
+            <s:simpleType name="ReservationStatus">
423
+                <s:restriction base="s:string">
424
+                    <s:enumeration value="Accepted"/>
425
+                    <s:enumeration value="Faulted"/>
426
+                    <s:enumeration value="Occupied"/>
427
+                    <s:enumeration value="Rejected"/>
428
+                    <s:enumeration value="Unavailable"/>
429
+                </s:restriction>
430
+            </s:simpleType>
431
+
432
+            <s:complexType name="ReserveNowResponse">
433
+                <s:annotation>
434
+                    <s:documentation>Defines the ReserveNow.conf PDU</s:documentation>
435
+                </s:annotation>
436
+                <s:sequence>
437
+                    <s:element name="status" type="tns:ReservationStatus" minOccurs="1" maxOccurs="1" />
438
+                </s:sequence>
439
+            </s:complexType>
440
+
441
+            <s:complexType name="AuthorisationData">
442
+                <s:sequence>
443
+                    <s:element name="idTag" type="tns:IdToken" minOccurs="1" maxOccurs="1"/>
444
+                    <s:element name="idTagInfo" type="tns:IdTagInfo" minOccurs="0" maxOccurs="1"/>
445
+                </s:sequence>
446
+            </s:complexType>
447
+
448
+            <s:simpleType name="UpdateType">
449
+                <s:restriction base="s:string">
450
+                    <s:enumeration value="Differential"/>
451
+                    <s:enumeration value="Full"/>
452
+                </s:restriction>
453
+            </s:simpleType>
454
+
455
+            <s:complexType name="SendLocalListRequest">
456
+                <s:annotation>
457
+                    <s:documentation>Defines the SendLocalList.req PDU</s:documentation>
458
+                </s:annotation>
459
+                <s:sequence>
460
+                    <s:element name="updateType" type="tns:UpdateType" minOccurs="1" maxOccurs="1" />
461
+                    <s:element name="listVersion" type="s:int" minOccurs="1" maxOccurs="1" />
462
+                    <s:element name="localAuthorisationList" type="tns:AuthorisationData" minOccurs="0" maxOccurs="unbounded" />
463
+                    <s:element name="hash" type="s:string" minOccurs="0" maxOccurs="1" />
464
+                </s:sequence>
465
+            </s:complexType>
466
+
467
+            <s:simpleType name="UpdateStatus">
468
+                <s:restriction base="s:string">
469
+                    <s:enumeration value="Accepted"/>
470
+                    <s:enumeration value="Failed"/>
471
+                    <s:enumeration value="HashError"/>
472
+                    <s:enumeration value="NotSupported"/>
473
+                    <s:enumeration value="VersionMismatch"/>
474
+                </s:restriction>
475
+            </s:simpleType>
476
+
477
+            <s:complexType name="SendLocalListResponse">
478
+                <s:annotation>
479
+                    <s:documentation>Defines the SendLocalList.conf PDU</s:documentation>
480
+                </s:annotation>
481
+                <s:sequence>
482
+                    <s:element name="status" type="tns:UpdateStatus" minOccurs="1" maxOccurs="1" />
483
+                    <s:element name="hash" type="s:string" minOccurs="0" maxOccurs="1" />
484
+                </s:sequence>
485
+            </s:complexType>
486
+
487
+            <s:element name="chargeBoxIdentity" type="s:string" />
488
+            <s:element name="unlockConnectorRequest" type="tns:UnlockConnectorRequest" />
489
+            <s:element name="unlockConnectorResponse" type="tns:UnlockConnectorResponse" />
490
+            <s:element name="resetRequest" type="tns:ResetRequest" />
491
+            <s:element name="resetResponse" type="tns:ResetResponse" />
492
+            <s:element name="changeAvailabilityRequest" type="tns:ChangeAvailabilityRequest" />
493
+            <s:element name="changeAvailabilityResponse" type="tns:ChangeAvailabilityResponse" />
494
+            <s:element name="getDiagnosticsRequest" type="tns:GetDiagnosticsRequest" />
495
+            <s:element name="getDiagnosticsResponse" type="tns:GetDiagnosticsResponse" />
496
+            <s:element name="clearCacheRequest" type="tns:ClearCacheRequest" />
497
+            <s:element name="clearCacheResponse" type="tns:ClearCacheResponse" />
498
+            <s:element name="updateFirmwareRequest" type="tns:UpdateFirmwareRequest" />
499
+            <s:element name="updateFirmwareResponse" type="tns:UpdateFirmwareResponse" />
500
+            <s:element name="changeConfigurationRequest" type="tns:ChangeConfigurationRequest" />
501
+            <s:element name="changeConfigurationResponse" type="tns:ChangeConfigurationResponse" />
502
+            <s:element name="remoteStartTransactionRequest" type="tns:RemoteStartTransactionRequest" />
503
+            <s:element name="remoteStartTransactionResponse" type="tns:RemoteStartTransactionResponse" />
504
+            <s:element name="remoteStopTransactionRequest" type="tns:RemoteStopTransactionRequest" />
505
+            <s:element name="remoteStopTransactionResponse" type="tns:RemoteStopTransactionResponse" />
506
+            <s:element name="cancelReservationRequest" type="tns:CancelReservationRequest" />
507
+            <s:element name="cancelReservationResponse" type="tns:CancelReservationResponse" />
508
+            <s:element name="dataTransferRequest" type="tns:DataTransferRequest" />
509
+            <s:element name="dataTransferResponse" type="tns:DataTransferResponse" />
510
+            <s:element name="getConfigurationRequest" type="tns:GetConfigurationRequest" />
511
+            <s:element name="getConfigurationResponse" type="tns:GetConfigurationResponse" />
512
+            <s:element name="getLocalListVersionRequest" type="tns:GetLocalListVersionRequest" />
513
+            <s:element name="getLocalListVersionResponse" type="tns:GetLocalListVersionResponse" />
514
+            <s:element name="reserveNowRequest" type="tns:ReserveNowRequest" />
515
+            <s:element name="reserveNowResponse" type="tns:ReserveNowResponse" />
516
+            <s:element name="sendLocalListRequest" type="tns:SendLocalListRequest" />
517
+            <s:element name="sendLocalListResponse" type="tns:SendLocalListResponse" />
518
+        </s:schema>
519
+    </wsdl:types>
520
+
521
+    <wsdl:message name="Header">
522
+        <wsdl:part name="ChargeBoxIdentity" element="tns:chargeBoxIdentity" />
523
+    </wsdl:message>
524
+
525
+    <wsdl:message name="UnlockConnectorInput">
526
+        <wsdl:part name="parameters" element="tns:unlockConnectorRequest" />
527
+    </wsdl:message>
528
+
529
+    <wsdl:message name="UnlockConnectorOutput">
530
+        <wsdl:part name="parameters" element="tns:unlockConnectorResponse" />
531
+    </wsdl:message>
532
+
533
+    <wsdl:message name="ResetInput">
534
+        <wsdl:part name="parameters" element="tns:resetRequest" />
535
+    </wsdl:message>
536
+
537
+    <wsdl:message name="ResetOutput">
538
+        <wsdl:part name="parameters" element="tns:resetResponse" />
539
+    </wsdl:message>
540
+
541
+    <wsdl:message name="ChangeAvailabilityInput">
542
+        <wsdl:part name="parameters" element="tns:changeAvailabilityRequest" />
543
+    </wsdl:message>
544
+
545
+    <wsdl:message name="ChangeAvailabilityOutput">
546
+        <wsdl:part name="parameters" element="tns:changeAvailabilityResponse" />
547
+    </wsdl:message>
548
+
549
+    <wsdl:message name="GetDiagnosticsInput">
550
+        <wsdl:part name="parameters" element="tns:getDiagnosticsRequest" />
551
+    </wsdl:message>
552
+
553
+    <wsdl:message name="GetDiagnosticsOutput">
554
+        <wsdl:part name="parameters" element="tns:getDiagnosticsResponse" />
555
+    </wsdl:message>
556
+
557
+    <wsdl:message name="ClearCacheInput">
558
+        <wsdl:part name="parameters" element="tns:clearCacheRequest" />
559
+    </wsdl:message>
560
+
561
+    <wsdl:message name="ClearCacheOutput">
562
+        <wsdl:part name="parameters" element="tns:clearCacheResponse" />
563
+    </wsdl:message>
564
+
565
+    <wsdl:message name="UpdateFirmwareInput">
566
+        <wsdl:part name="parameters" element="tns:updateFirmwareRequest" />
567
+    </wsdl:message>
568
+
569
+    <wsdl:message name="UpdateFirmwareOutput">
570
+        <wsdl:part name="parameters" element="tns:updateFirmwareResponse" />
571
+    </wsdl:message>
572
+
573
+    <wsdl:message name="ChangeConfigurationInput">
574
+        <wsdl:part name="parameters" element="tns:changeConfigurationRequest" />
575
+    </wsdl:message>
576
+
577
+    <wsdl:message name="ChangeConfigurationOutput">
578
+        <wsdl:part name="parameters" element="tns:changeConfigurationResponse" />
579
+    </wsdl:message>
580
+
581
+    <wsdl:message name="RemoteStartTransactionInput">
582
+        <wsdl:part name="parameters" element="tns:remoteStartTransactionRequest" />
583
+    </wsdl:message>
584
+
585
+    <wsdl:message name="RemoteStartTransactionOutput">
586
+        <wsdl:part name="parameters" element="tns:remoteStartTransactionResponse" />
587
+    </wsdl:message>
588
+
589
+    <wsdl:message name="RemoteStopTransactionInput">
590
+        <wsdl:part name="parameters" element="tns:remoteStopTransactionRequest" />
591
+    </wsdl:message>
592
+
593
+    <wsdl:message name="RemoteStopTransactionOutput">
594
+        <wsdl:part name="parameters" element="tns:remoteStopTransactionResponse" />
595
+    </wsdl:message>
596
+
597
+    <wsdl:message name="CancelReservationInput">
598
+        <wsdl:part name="parameters" element="tns:cancelReservationRequest" />
599
+    </wsdl:message>
600
+
601
+    <wsdl:message name="CancelReservationOutput">
602
+        <wsdl:part name="parameters" element="tns:cancelReservationResponse" />
603
+    </wsdl:message>
604
+
605
+    <wsdl:message name="DataTransferInput">
606
+        <wsdl:part name="parameters" element="tns:dataTransferRequest" />
607
+    </wsdl:message>
608
+
609
+    <wsdl:message name="DataTransferOutput">
610
+        <wsdl:part name="parameters" element="tns:dataTransferResponse" />
611
+    </wsdl:message>
612
+
613
+    <wsdl:message name="GetConfigurationInput">
614
+        <wsdl:part name="parameters" element="tns:getConfigurationRequest" />
615
+    </wsdl:message>
616
+
617
+    <wsdl:message name="GetConfigurationOutput">
618
+        <wsdl:part name="parameters" element="tns:getConfigurationResponse" />
619
+    </wsdl:message>
620
+
621
+    <wsdl:message name="GetLocalListVersionInput">
622
+        <wsdl:part name="parameters" element="tns:getLocalListVersionRequest" />
623
+    </wsdl:message>
624
+
625
+    <wsdl:message name="GetLocalListVersionOutput">
626
+        <wsdl:part name="parameters" element="tns:getLocalListVersionResponse" />
627
+    </wsdl:message>
628
+
629
+    <wsdl:message name="ReserveNowInput">
630
+        <wsdl:part name="parameters" element="tns:reserveNowRequest" />
631
+    </wsdl:message>
632
+
633
+    <wsdl:message name="ReserveNowOutput">
634
+        <wsdl:part name="parameters" element="tns:reserveNowResponse" />
635
+    </wsdl:message>
636
+
637
+    <wsdl:message name="SendLocalListInput">
638
+        <wsdl:part name="parameters" element="tns:sendLocalListRequest" />
639
+    </wsdl:message>
640
+
641
+    <wsdl:message name="SendLocalListOutput">
642
+        <wsdl:part name="parameters" element="tns:sendLocalListResponse" />
643
+    </wsdl:message>
644
+
645
+    <wsdl:portType name="ChargePointService">
646
+
647
+        <wsdl:operation name="UnlockConnector">
648
+            <wsdl:input message="tns:UnlockConnectorInput" wsaw:Action="/UnlockConnector" />
649
+            <wsdl:output message="tns:UnlockConnectorOutput" wsaw:Action="/UnlockConnectorResponse" />
650
+        </wsdl:operation>
651
+
652
+        <wsdl:operation name="Reset">
653
+            <wsdl:input message="tns:ResetInput" wsaw:Action="/Reset" />
654
+            <wsdl:output message="tns:ResetOutput" wsaw:Action="/ResetResponse" />
655
+        </wsdl:operation>
656
+
657
+        <wsdl:operation name="ChangeAvailability">
658
+            <wsdl:input message="tns:ChangeAvailabilityInput" wsaw:Action="/ChangeAvailability" />
659
+            <wsdl:output message="tns:ChangeAvailabilityOutput" wsaw:Action="/ChangeAvailabilityResponse" />
660
+        </wsdl:operation>
661
+
662
+        <wsdl:operation name="GetDiagnostics">
663
+            <wsdl:input message="tns:GetDiagnosticsInput" wsaw:Action="/GetDiagnostics" />
664
+            <wsdl:output message="tns:GetDiagnosticsOutput" wsaw:Action="/GetDiagnosticsResponse" />
665
+        </wsdl:operation>
666
+
667
+        <wsdl:operation name="ClearCache">
668
+            <wsdl:input message="tns:ClearCacheInput" wsaw:Action="/ClearCache" />
669
+            <wsdl:output message="tns:ClearCacheOutput" wsaw:Action="/ClearCacheResponse" />
670
+        </wsdl:operation>
671
+
672
+        <wsdl:operation name="UpdateFirmware">
673
+            <wsdl:input message="tns:UpdateFirmwareInput" wsaw:Action="/UpdateFirmware" />
674
+            <wsdl:output message="tns:UpdateFirmwareOutput" wsaw:Action="/UpdateFirmwareResponse" />
675
+        </wsdl:operation>
676
+
677
+        <wsdl:operation name="ChangeConfiguration">
678
+            <wsdl:input message="tns:ChangeConfigurationInput" wsaw:Action="/ChangeConfiguration" />
679
+            <wsdl:output message="tns:ChangeConfigurationOutput" wsaw:Action="/ChangeConfigurationResponse" />
680
+        </wsdl:operation>
681
+
682
+        <wsdl:operation name="RemoteStartTransaction">
683
+            <wsdl:input message="tns:RemoteStartTransactionInput" wsaw:Action="/RemoteStartTransaction" />
684
+            <wsdl:output message="tns:RemoteStartTransactionOutput" wsaw:Action="/RemoteStartTransactionResponse" />
685
+        </wsdl:operation>
686
+
687
+        <wsdl:operation name="RemoteStopTransaction">
688
+            <wsdl:input message="tns:RemoteStopTransactionInput" wsaw:Action="/RemoteStopTransaction" />
689
+            <wsdl:output message="tns:RemoteStopTransactionOutput" wsaw:Action="/RemoteStopTransactionResponse" />
690
+        </wsdl:operation>
691
+
692
+        <wsdl:operation name="CancelReservation">
693
+            <wsdl:input message="tns:CancelReservationInput" wsaw:Action="/CancelReservation" />
694
+            <wsdl:output message="tns:CancelReservationOutput" wsaw:Action="/CancelReservationResponse" />
695
+        </wsdl:operation>
696
+
697
+        <wsdl:operation name="DataTransfer">
698
+            <wsdl:input message="tns:DataTransferInput" wsaw:Action="/DataTransfer" />
699
+            <wsdl:output message="tns:DataTransferOutput" wsaw:Action="/DataTransferResponse" />
700
+        </wsdl:operation>
701
+
702
+        <wsdl:operation name="GetConfiguration">
703
+            <wsdl:input message="tns:GetConfigurationInput" wsaw:Action="/GetConfiguration" />
704
+            <wsdl:output message="tns:GetConfigurationOutput" wsaw:Action="/GetConfigurationResponse" />
705
+        </wsdl:operation>
706
+
707
+        <wsdl:operation name="GetLocalListVersion">
708
+            <wsdl:input message="tns:GetLocalListVersionInput" wsaw:Action="/GetLocalListVersion" />
709
+            <wsdl:output message="tns:GetLocalListVersionOutput" wsaw:Action="/GetLocalListVersionResponse" />
710
+        </wsdl:operation>
711
+
712
+        <wsdl:operation name="ReserveNow">
713
+            <wsdl:input message="tns:ReserveNowInput" wsaw:Action="/ReserveNow" />
714
+            <wsdl:output message="tns:ReserveNowOutput" wsaw:Action="/ReserveNowResponse" />
715
+        </wsdl:operation>
716
+
717
+        <wsdl:operation name="SendLocalList">
718
+            <wsdl:input message="tns:SendLocalListInput" wsaw:Action="/SendLocalList" />
719
+            <wsdl:output message="tns:SendLocalListOutput" wsaw:Action="/SendLocalListResponse" />
720
+        </wsdl:operation>
721
+
722
+    </wsdl:portType>
723
+
724
+    <wsdl:binding name="ChargePointServiceSoap" type="tns:ChargePointService">
725
+
726
+        <wsp:PolicyReference URI="#ServicePolicy" />
727
+        <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
728
+
729
+        <wsdl:operation name="UnlockConnector">
730
+            <soap12:operation soapAction="/UnlockConnector" style="document" />
731
+            <wsdl:input>
732
+                <soap12:header use="literal" message="tns:Header" part="ChargeBoxIdentity"/>
733
+                <soap12:body use="literal" />
734
+            </wsdl:input>
735
+            <wsdl:output>
736
+                <soap12:body use="literal" />
737
+            </wsdl:output>
738
+        </wsdl:operation>
739
+
740
+        <wsdl:operation name="Reset">
741
+            <soap12:operation soapAction="/Reset" style="document" />
742
+            <wsdl:input>
743
+                <soap12:header use="literal" message="tns:Header" part="ChargeBoxIdentity"/>
744
+                <soap12:body use="literal" />
745
+            </wsdl:input>
746
+            <wsdl:output>
747
+                <soap12:body use="literal" />
748
+            </wsdl:output>
749
+        </wsdl:operation>
750
+
751
+        <wsdl:operation name="ChangeAvailability">
752
+            <soap12:operation soapAction="/ChangeAvailability" style="document" />
753
+            <wsdl:input>
754
+                <soap12:header use="literal" message="tns:Header" part="ChargeBoxIdentity"/>
755
+                <soap12:body use="literal" />
756
+            </wsdl:input>
757
+            <wsdl:output>
758
+                <soap12:body use="literal" />
759
+            </wsdl:output>
760
+        </wsdl:operation>
761
+
762
+        <wsdl:operation name="GetDiagnostics">
763
+            <soap12:operation soapAction="/GetDiagnostics" style="document" />
764
+            <wsdl:input>
765
+                <soap12:header use="literal" message="tns:Header" part="ChargeBoxIdentity"/>
766
+                <soap12:body use="literal" />
767
+            </wsdl:input>
768
+            <wsdl:output>
769
+                <soap12:body use="literal" />
770
+            </wsdl:output>
771
+        </wsdl:operation>
772
+
773
+        <wsdl:operation name="ClearCache">
774
+            <soap12:operation soapAction="/ClearCache" style="document" />
775
+            <wsdl:input>
776
+                <soap12:header use="literal" message="tns:Header" part="ChargeBoxIdentity"/>
777
+                <soap12:body use="literal" />
778
+            </wsdl:input>
779
+            <wsdl:output>
780
+                <soap12:body use="literal" />
781
+            </wsdl:output>
782
+        </wsdl:operation>
783
+
784
+        <wsdl:operation name="UpdateFirmware">
785
+            <soap12:operation soapAction="/UpdateFirmware" style="document" />
786
+            <wsdl:input>
787
+                <soap12:header use="literal" message="tns:Header" part="ChargeBoxIdentity"/>
788
+                <soap12:body use="literal" />
789
+            </wsdl:input>
790
+            <wsdl:output>
791
+                <soap12:body use="literal" />
792
+            </wsdl:output>
793
+        </wsdl:operation>
794
+
795
+        <wsdl:operation name="ChangeConfiguration">
796
+            <soap12:operation soapAction="/ChangeConfiguration" style="document" />
797
+            <wsdl:input>
798
+                <soap12:header use="literal" message="tns:Header" part="ChargeBoxIdentity"/>
799
+                <soap12:body use="literal" />
800
+            </wsdl:input>
801
+            <wsdl:output>
802
+                <soap12:body use="literal" />
803
+            </wsdl:output>
804
+        </wsdl:operation>
805
+
806
+        <wsdl:operation name="RemoteStartTransaction">
807
+            <soap12:operation soapAction="/RemoteStartTransaction" style="document" />
808
+            <wsdl:input>
809
+                <soap12:header use="literal" message="tns:Header" part="ChargeBoxIdentity"/>
810
+                <soap12:body use="literal" />
811
+            </wsdl:input>
812
+            <wsdl:output>
813
+                <soap12:body use="literal" />
814
+            </wsdl:output>
815
+        </wsdl:operation>
816
+
817
+        <wsdl:operation name="RemoteStopTransaction">
818
+            <soap12:operation soapAction="/RemoteStopTransaction" style="document" />
819
+            <wsdl:input>
820
+                <soap12:header use="literal" message="tns:Header" part="ChargeBoxIdentity"/>
821
+                <soap12:body use="literal" />
822
+            </wsdl:input>
823
+            <wsdl:output>
824
+                <soap12:body use="literal" />
825
+            </wsdl:output>
826
+        </wsdl:operation>
827
+
828
+        <wsdl:operation name="CancelReservation">
829
+            <soap12:operation soapAction="/CancelReservation" style="document" />
830
+            <wsdl:input>
831
+                <soap12:header use="literal" message="tns:Header" part="ChargeBoxIdentity"/>
832
+                <soap12:body use="literal" />
833
+            </wsdl:input>
834
+            <wsdl:output>
835
+                <soap12:body use="literal" />
836
+            </wsdl:output>
837
+        </wsdl:operation>
838
+
839
+        <wsdl:operation name="DataTransfer">
840
+            <soap12:operation soapAction="/DataTransfer" style="document" />
841
+            <wsdl:input>
842
+                <soap12:header use="literal" message="tns:Header" part="ChargeBoxIdentity"/>
843
+                <soap12:body use="literal" />
844
+            </wsdl:input>
845
+            <wsdl:output>
846
+                <soap12:body use="literal" />
847
+            </wsdl:output>
848
+        </wsdl:operation>
849
+
850
+        <wsdl:operation name="GetConfiguration">
851
+            <soap12:operation soapAction="/GetConfiguration" style="document" />
852
+            <wsdl:input>
853
+                <soap12:header use="literal" message="tns:Header" part="ChargeBoxIdentity"/>
854
+                <soap12:body use="literal" />
855
+            </wsdl:input>
856
+            <wsdl:output>
857
+                <soap12:body use="literal" />
858
+            </wsdl:output>
859
+        </wsdl:operation>
860
+
861
+        <wsdl:operation name="GetLocalListVersion">
862
+            <soap12:operation soapAction="/GetLocalListVersion" style="document" />
863
+            <wsdl:input>
864
+                <soap12:header use="literal" message="tns:Header" part="ChargeBoxIdentity"/>
865
+                <soap12:body use="literal" />
866
+            </wsdl:input>
867
+            <wsdl:output>
868
+                <soap12:body use="literal" />
869
+            </wsdl:output>
870
+        </wsdl:operation>
871
+
872
+        <wsdl:operation name="ReserveNow">
873
+            <soap12:operation soapAction="/ReserveNow" style="document" />
874
+            <wsdl:input>
875
+                <soap12:header use="literal" message="tns:Header" part="ChargeBoxIdentity"/>
876
+                <soap12:body use="literal" />
877
+            </wsdl:input>
878
+            <wsdl:output>
879
+                <soap12:body use="literal" />
880
+            </wsdl:output>
881
+        </wsdl:operation>
882
+
883
+        <wsdl:operation name="SendLocalList">
884
+            <soap12:operation soapAction="/SendLocalList" style="document" />
885
+            <wsdl:input>
886
+                <soap12:header use="literal" message="tns:Header" part="ChargeBoxIdentity"/>
887
+                <soap12:body use="literal" />
888
+            </wsdl:input>
889
+            <wsdl:output>
890
+                <soap12:body use="literal" />
891
+            </wsdl:output>
892
+        </wsdl:operation>
893
+
894
+    </wsdl:binding>
895
+
896
+
897
+    <!-- The OCPP ChargePoint service -->
898
+    <wsdl:service name="ChargePointService">
899
+        <wsdl:documentation>The ChargePoint Service for the Open Charge Point Protocol</wsdl:documentation>
900
+        <wsdl:port name="ChargePointServiceSoap12" binding="tns:ChargePointServiceSoap">
901
+            <soap12:address location="http://localhost:9000/Ocpp/ChargePointService/" />
902
+        </wsdl:port>
903
+    </wsdl:service>
904
+</wsdl:definitions>

+ 20 - 0
charger-simulator/src/soap/xmlUtils.ts

@@ -0,0 +1,20 @@
1
+import {Builder, parseString} from "xml2js"
2
+
3
+export async function prettyPrintXml(xml): Promise<string> {
4
+  return new Promise<string>((resolve, reject) => {
5
+    parseString(xml, (err, parsed) => {
6
+      if (err) {
7
+        reject(err)
8
+        return
9
+      }
10
+
11
+      try {
12
+        const formatted = new Builder({headless: true}).buildObject(parsed)
13
+
14
+        resolve("\n" + formatted.replace(/n\/  \n/g))
15
+      } catch (e) {
16
+        reject(e)
17
+      }
18
+    })
19
+  })
20
+}

+ 12 - 0
charger-simulator/tsconfig.json

@@ -0,0 +1,12 @@
1
+{
2
+  "compilerOptions": {
3
+    "target": "es5",
4
+    "module": "CommonJS",
5
+    "outDir": "dist",
6
+    "lib": [
7
+      "es6"
8
+    ],
9
+    "declaration": true
10
+  },
11
+  "include": ["src/**/*.ts"]
12
+}

Datei-Diff unterdrückt, da er zu groß ist
+ 1094 - 0
charger-simulator/yarn.lock


+ 65 - 0
cs.py

@@ -0,0 +1,65 @@
1
+import asyncio
2
+import logging
3
+import websockets
4
+from datetime import datetime
5
+
6
+from ocpp.routing import on
7
+from ocpp.v201 import ChargePoint as cp
8
+from ocpp.v201 import call_result
9
+from ocpp.v201.enums import RegistrationStatusType
10
+
11
+logging.basicConfig(level=logging.INFO)
12
+
13
+
14
+class ChargePoint(cp):
15
+    @on('BootNotification')
16
+    async def on_boot_notification(self, charging_station, reason, **kwargs):
17
+        return call_result.BootNotificationPayload(
18
+            current_time=datetime.utcnow().isoformat(),
19
+            interval=10,
20
+            status=RegistrationStatusType.accepted
21
+        )
22
+
23
+
24
+async def on_connect(websocket, path):
25
+    """ For every new charge point that connects, create a ChargePoint
26
+    instance and start listening for messages.
27
+    """
28
+    try:
29
+        requested_protocols = websocket.request_headers[
30
+            'Sec-WebSocket-Protocol']
31
+    except KeyError:
32
+        logging.info("Client hasn't requested any Subprotocol. "
33
+                 "Closing Connection")
34
+        return await websocket.close()
35
+
36
+    if websocket.subprotocol:
37
+        logging.info("Protocols Matched: %s", websocket.subprotocol)
38
+    else:
39
+        # In the websockets lib if no subprotocols are supported by the
40
+        # client and the server, it proceeds without a subprotocol,
41
+        # so we have to manually close the connection.
42
+        logging.warning('Protocols Mismatched | Expected Subprotocols: %s,'
43
+                        ' but client supports  %s | Closing connection',
44
+                        websocket.available_subprotocols,
45
+                        requested_protocols)
46
+        return await websocket.close()
47
+
48
+    charge_point_id = path.strip('/')
49
+    cp = ChargePoint(charge_point_id, websocket)
50
+
51
+    await cp.start()
52
+
53
+
54
+async def main():
55
+    server = await websockets.serve(
56
+        on_connect,
57
+        '0.0.0.0',
58
+        9000,
59
+        subprotocols=['ocpp1.6']
60
+    )
61
+    logging.info("WebSocket Server Started")
62
+    await server.wait_closed()
63
+
64
+if __name__ == '__main__':
65
+    asyncio.run(main())

+ 190 - 0
cs1_6.py

@@ -0,0 +1,190 @@
1
+import asyncio
2
+import logging
3
+from datetime import datetime
4
+from pprint import pprint
5
+
6
+try:
7
+	import websockets
8
+except ModuleNotFoundError:
9
+	print("This example relies on the 'websockets' package.")
10
+	print("Please install it by running: ")
11
+	print()
12
+	print(" $ pip install websockets")
13
+	import sys
14
+
15
+	sys.exit(1)
16
+
17
+from ocpp.routing import on, after
18
+from ocpp.v16 import ChargePoint as cp
19
+from ocpp.v16 import call_result
20
+from ocpp.v16.datatypes import IdTagInfo
21
+from ocpp.v16.enums import Action, RegistrationStatus, AuthorizationStatus
22
+from ocpp.messages import Call, MessageType, unpack, validate_payload
23
+
24
+
25
+logging.basicConfig(level=logging.INFO)
26
+
27
+class CustomCP(cp):
28
+    def __init__(self, id, connection, response_timeout=30):
29
+        super().__init__(id, connection, response_timeout)
30
+
31
+    async def _handle_call(self, msg):
32
+        """
33
+        Execute all hooks installed for based on the Action of the message.
34
+
35
+        First the '_on_action' hook is executed and its response is returned to
36
+        the client. If there is no '_on_action' hook for Action in the message
37
+        a CallError with a NotImplementedError is returned. If the Action is
38
+        not supported by the OCPP version a NotSupportedError is returned.
39
+
40
+        Next the '_after_action' hook is executed.
41
+
42
+        """
43
+        try:
44
+            handlers = self.route_map[msg.action]
45
+        except KeyError:
46
+            _raise_key_error(msg.action, self._ocpp_version)
47
+            return
48
+
49
+        if not handlers.get("_skip_schema_validation", False):
50
+            validate_payload(msg, self._ocpp_version)
51
+        # OCPP uses camelCase for the keys in the payload. It's more pythonic
52
+        # to use snake_case for keyword arguments. Therefore the keys must be
53
+        # 'translated'. Some examples:
54
+        #
55
+        # * chargePointVendor becomes charge_point_vendor
56
+        # * firmwareVersion becomes firmwareVersion
57
+        if msg.payload is None:
58
+            msg.payload = {}
59
+        snake_case_payload = camel_to_snake_case(msg.payload)
60
+
61
+        try:
62
+            handler = handlers["_on_action"]
63
+        except KeyError:
64
+            _raise_key_error(msg.action, self._ocpp_version)
65
+
66
+        try:
67
+            response = handler(**snake_case_payload)
68
+            if inspect.isawaitable(response):
69
+                response = await response
70
+        except Exception as e:
71
+            LOGGER.exception("Error while handling request '%s'", msg)
72
+            response = msg.create_call_error(e).to_json()
73
+            await self._send(response)
74
+
75
+            return
76
+
77
+        temp_response_payload = asdict(response)
78
+
79
+        # Remove nones ensures that we strip out optional arguments
80
+        # which were not set and have a default value of None
81
+        response_payload = remove_nones(temp_response_payload)
82
+
83
+        # The response payload must be 'translated' from snake_case to
84
+        # camelCase. So:
85
+        #
86
+        # * charge_point_vendor becomes chargePointVendor
87
+        # * firmware_version becomes firmwareVersion
88
+        camel_case_payload = snake_to_camel_case(response_payload)
89
+
90
+        response = msg.create_call_result(camel_case_payload)
91
+
92
+        if not handlers.get("_skip_schema_validation", False):
93
+            validate_payload(response, self._ocpp_version)
94
+
95
+        await self._send(response.to_json())
96
+
97
+        try:
98
+            handler = handlers["_after_action"]
99
+            # Create task to avoid blocking when making a call inside the
100
+            # after handler
101
+            response = handler(**snake_case_payload)
102
+            if inspect.isawaitable(response):
103
+                asyncio.ensure_future(response)
104
+        except KeyError:
105
+            # '_on_after' hooks are not required. Therefore ignore exception
106
+            # when no '_on_after' hook is installed.
107
+            pass
108
+        return response
109
+	
110
+
111
+class ChargePoint(cp):
112
+
113
+	@on(Action.BootNotification)
114
+	def on_boot_notification(
115
+			self, charge_point_vendor: str, charge_point_model: str, **kwargs):
116
+		pprint(kwargs)
117
+		return call_result.BootNotificationPayload(
118
+				current_time=datetime.utcnow().isoformat(),
119
+				interval=10,
120
+				status=RegistrationStatus.accepted,
121
+				)
122
+
123
+	@on(Action.Authorize)
124
+	def on_authorization(self, id_tag):
125
+		# pprint(kwargs)
126
+		idti = IdTagInfo(status=AuthorizationStatus.accepted)
127
+		# idti.status = AuthorizationStatus.accepted
128
+
129
+		return call_result.AuthorizePayload(
130
+				id_tag_info=idti
131
+				)
132
+
133
+	@on(Action.StatusNotification)
134
+	def on_status_notification(self, id_tag):
135
+		# pprint(kwargs)
136
+		# idti = IdTagInfo(status=AuthorizationStatus.accepted)
137
+		# idti.status = AuthorizationStatus.accepted
138
+		return call_result.StatusNotificationPayload()
139
+
140
+	@on(Action.Heartbeat, skip_schema_validation=True)
141
+	def on_heartbeat(self, **kwargs):  # receives empty payload from CP
142
+		print("--HeartBeat--")
143
+		print(kwargs)
144
+		return call_result.HeartbeatPayload(
145
+				current_time=datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
146
+				)
147
+
148
+
149
+
150
+async def on_connect(websocket, path):
151
+	"""For every new charge point that connects, create a ChargePoint
152
+	instance and start listening for messages.
153
+	"""
154
+	try:
155
+		requested_protocols = websocket.request_headers["Sec-WebSocket-Protocol"]
156
+	except KeyError:
157
+		logging.error("Client hasn't requested any Subprotocol. Closing Connection")
158
+		return await websocket.close()
159
+	if websocket.subprotocol:
160
+		logging.info("Protocols Matched: %s", websocket.subprotocol)
161
+	else:
162
+		# In the websockets lib if no subprotocols are supported by the
163
+		# client and the server, it proceeds without a subprotocol,
164
+		# so we have to manually close the connection.
165
+		logging.warning(
166
+				"Protocols Mismatched | Expected Subprotocols: %s,"
167
+				" but client supports  %s | Closing connection",
168
+				websocket.available_subprotocols,
169
+				requested_protocols,
170
+				)
171
+		return await websocket.close()
172
+
173
+	charge_point_id = path.strip("/")
174
+	cp = ChargePoint(charge_point_id, websocket)
175
+
176
+	await cp.start()
177
+
178
+
179
+async def main():
180
+	server = await websockets.serve(
181
+			on_connect, "0.0.0.0", 9000, subprotocols=["ocpp1.6"]
182
+			)
183
+
184
+	logging.info("Server Started listening to new connections...")
185
+	await server.wait_closed()
186
+
187
+
188
+if __name__ == "__main__":
189
+	# asyncio.run() is used when running this example with Python >= 3.7v
190
+	asyncio.run(main())

+ 1 - 0
ocpp

@@ -0,0 +1 @@
1
+Subproject commit a9df8ebdaee922c323be1d06eace2d5a35a5f747