Skip to content

Commit d465252

Browse files
Load standard values in server in testcases #2 (#261)
* added small int support * change order * spaces between functions * changed functions small_int to usint (unsigned small int) * change in db_row get value y set value changed comparation from sint to usint (0-255) * added to db_row get_value and set_value comparation to sint (-128 to 127) * added support for SMALL INT (-127 to 128) * changes in test_util in bytearray, added numbers to ocuppe all bytes of the data structure in DTL * test passed | changed _bytearray in test to match the lenght for the test * added doc * change space * arreglos de espacios en blanco * added s7areaDB for default * added default to db and db_row -> area=S7AreaDB * same * added read area | write area --- TODO tests * correction to the db1['test'].read() -> added client * read area debe corregirse * correction to client.read_area of function write in class db_row * corrections to def write in class db_row * Small changes - Types.py: * Added enum class to handle areas - Utils: * Added some missing types * Added area parameter to the DB class, this way you could handle the inputs, outputs, or marks as a DB for reading it TODO: add tests - Common.py: * Missing types - Client.py: * Added an optional init parameter for the lib location, this way people who can't instal properly the library, they can specify the path to the .dll file. * Types.py readd S7Areas as alone values * Test fixes 1 * Typing errors fix 2 * White spaces fixes * White spaces * Remove enum Areas, so we stick to the ADict class * Remove it TODOs in util.py * Utils type export fix * _type parameter switch to type_ * Fix typing for areas Adict * Migration to Enum for Areas and WordLen * switch assert date * whitespaces * Fix typing errors in util file * ignore typing * fix test * whitespaces * Added find locally snap7.dll Fix logo errors * fix assert error in date * types.py: * remove it string multiline methods added- common.py * Remove it type ignore from ctypes import * Added common test and change common find_locally from using os to pathlib * Pathlib * Path lib fix to find in the current working directory * prefill server areas for testing * como crear un arreglo desde un buffer * ... * test mainloop prefill working until double int * Working filled values for: - DWORD - WORD - STRING - DINT - INT - USINT - SINT - BOOL * fix pycodestyle * Fixed test_read_real adding 0.00001% of almost equal assert * Server and test changes - Mainloop init standar values move to it's own function - Change how binary is written - Doc added? - Move each datatype to a round decimal place * fix pycodestyle * added test_mainloop to the makefile * also run tests on github actions Co-authored-by: Lautaro Dapino <[email protected]>
1 parent 2475cb1 commit d465252

File tree

6 files changed

+285
-5
lines changed

6 files changed

+285
-5
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,5 @@ jobs:
3232
- name: Run pytest
3333
run: |
3434
which pytest
35-
pytest test/test_server.py test/test_client.py test/test_util.py
35+
pytest test/test_server.py test/test_client.py test/test_util.py test/test_mainloop.py
3636
sudo /opt/hostedtoolcache/Python/${{ matrix.python-version }}*/x64/bin/pytest test/test_partner.py

.github/workflows/osx.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
run: python3 -m pip install .[test]
1919
- name: Run pytest
2020
run: |
21-
pytest test/test_server.py test/test_client.py test/test_util.py
21+
pytest test/test_server.py test/test_client.py test/test_util.py test/test_mainloop.py
2222
sudo pytest test/test_partner.py
2323
2424

.github/workflows/windows.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ jobs:
2424
- name: Install python libraries
2525
run: python.exe -m pip install .[test]
2626
- name: Run tests
27-
run: pytest.exe test/test_client.py test/test_util.py test/test_partner.py
27+
run: pytest.exe test/test_client.py test/test_util.py test/test_partner.py test/test_mainloop.py

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ mypy: venv/bin/pytest
3333
venv/bin/mypy snap7 test
3434

3535
test: venv/bin/pytest
36-
venv/bin/pytest test/test_server.py test/test_client.py test/test_util.py
36+
venv/bin/pytest test/test_server.py test/test_client.py test/test_util.py test/test_mainloop.py
3737
sudo venv/bin/pytest test/test_partner.py # run this as last to prevent pytest cache dir creates as root
3838

3939
clean:

snap7/server.py

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44
import logging
55
import re
6+
import struct
67
import time
78
import ctypes
89
from typing import Tuple, Optional, Callable, Any
@@ -290,7 +291,7 @@ def clear_events(self) -> int:
290291
return self.library.Srv_ClearEvents(self.pointer)
291292

292293

293-
def mainloop(tcpport: int = 1102):
294+
def mainloop(tcpport: int = 1102, init_standard_values: bool = False):
294295
server = snap7.server.Server()
295296
size = 100
296297
DBdata = (snap7.types.wordlen_to_ctypes[snap7.types.WordLen.Byte.value] * size)()
@@ -301,6 +302,13 @@ def mainloop(tcpport: int = 1102):
301302
server.register_area(snap7.types.srvAreaPA, 1, PAdata)
302303
server.register_area(snap7.types.srvAreaTM, 1, TMdata)
303304
server.register_area(snap7.types.srvAreaCT, 1, CTdata)
305+
306+
if init_standard_values:
307+
ba = _init_standard_values()
308+
DBdata = snap7.types.wordlen_to_ctypes[snap7.types.WordLen.Byte.value] * len(ba)
309+
DBdata = DBdata.from_buffer(ba)
310+
server.register_area(snap7.types.srvAreaDB, 0, DBdata)
311+
304312
server.start(tcpport=tcpport)
305313
while True:
306314
while True:
@@ -310,3 +318,136 @@ def mainloop(tcpport: int = 1102):
310318
else:
311319
break
312320
time.sleep(1)
321+
322+
323+
def _init_standard_values() -> bytearray:
324+
''' Standard values
325+
* Boolean
326+
BYTE BIT VALUE
327+
0 0 True
328+
0 1 False
329+
0 2 True
330+
0 3 False
331+
0 4 True
332+
0 5 False
333+
0 6 True
334+
0 7 False
335+
336+
* Small int
337+
BYTE VALUE
338+
10 -128
339+
11 0
340+
12 100
341+
13 127
342+
343+
* Unsigned small int
344+
BYTE VALUE
345+
20 0
346+
21 255
347+
348+
* Int
349+
BYTE VALUE
350+
30 -32768
351+
32 -1234
352+
34 0
353+
36 1234
354+
38 32767
355+
356+
* Double int
357+
BYTE VALUE
358+
40 -2147483648
359+
44 -32768
360+
48 0
361+
52 32767
362+
56 2147483647
363+
364+
* Real
365+
BYTE VALUE
366+
60 -3.402823e38
367+
64 -3.402823e12
368+
68 -175494351e-38
369+
72 -1.175494351e-12
370+
76 0.0
371+
80 1.175494351e-38
372+
84 1.175494351e-12
373+
88 3.402823466e12
374+
92 3.402823466e38
375+
376+
* String
377+
BYTE VALUE
378+
100 254|37|the brown fox jumps over the lazy dog
379+
380+
* Word
381+
BYTE VALUE
382+
400 \x00\x00
383+
404 \x12\x34
384+
408 \xAB\xCD
385+
412 \xFF\xFF
386+
387+
* Double Word
388+
BYTE VALUE
389+
500 \x00\x00\x00\x00
390+
508 \x12\x34\x56\x78
391+
516 \x12\x34\xAB\xCD
392+
524 \xFF\xFF\xFF\xFF
393+
'''
394+
395+
ba = bytearray(1000)
396+
# 1. Bool 1 byte
397+
ba[0] = 0b10101010
398+
399+
# 2. Small int 1 byte
400+
ba[10:10 + 1] = struct.pack(">b", -128)
401+
ba[11:11 + 1] = struct.pack(">b", 0)
402+
ba[12:12 + 1] = struct.pack(">b", 100)
403+
ba[13:13 + 1] = struct.pack(">b", 127)
404+
405+
# 3. Unsigned small int 1 byte
406+
ba[20:20 + 1] = struct.pack("B", 0)
407+
ba[21:21 + 1] = struct.pack("B", 255)
408+
409+
# 4. Int 2 bytes
410+
ba[30:30 + 2] = struct.pack(">h", -32768)
411+
ba[32:32 + 2] = struct.pack(">h", -1234)
412+
ba[34:34 + 2] = struct.pack(">h", 0)
413+
ba[36:36 + 2] = struct.pack(">h", 1234)
414+
ba[38:38 + 2] = struct.pack(">h", 32767)
415+
416+
# 5. DInt 4 bytes
417+
ba[40:40 + 4] = struct.pack(">i", -2147483648)
418+
ba[44:44 + 4] = struct.pack(">i", -32768)
419+
ba[48:48 + 4] = struct.pack(">i", 0)
420+
ba[52:52 + 4] = struct.pack(">i", 32767)
421+
ba[56:56 + 4] = struct.pack(">i", 2147483647)
422+
423+
# 6. Real 4 bytes
424+
ba[60:60 + 4] = struct.pack(">f", -3.402823e38)
425+
ba[64:64 + 4] = struct.pack(">f", -3.402823e12)
426+
ba[68:68 + 4] = struct.pack(">f", -175494351e-38)
427+
ba[72:72 + 4] = struct.pack(">f", -1.175494351e-12)
428+
ba[76:76 + 4] = struct.pack(">f", 0.0)
429+
ba[80:80 + 4] = struct.pack(">f", 1.175494351e-38)
430+
ba[84:84 + 4] = struct.pack(">f", 1.175494351e-12)
431+
ba[88:88 + 4] = struct.pack(">f", 3.402823466e12)
432+
ba[92:92 + 4] = struct.pack(">f", 3.402823466e38)
433+
434+
# 7. String 1 byte per char
435+
string = "the brown fox jumps over the lazy dog" # len = 37
436+
ba[100] = 254
437+
ba[101] = len(string)
438+
for letter, i in zip(string, range(102, 102 + len(string) + 1)):
439+
ba[i] = ord(letter)
440+
441+
# 8. WORD 4 bytes
442+
ba[400:400 + 4] = b"\x00\x00"
443+
ba[404:404 + 4] = b"\x12\x34"
444+
ba[408:408 + 4] = b"\xAB\xCD"
445+
ba[412:412 + 4] = b"\xFF\xFF"
446+
447+
# # 9 DWORD 8 bytes
448+
ba[500:500 + 8] = b"\x00\x00\x00\x00"
449+
ba[508:508 + 8] = b"\x12\x34\x56\x78"
450+
ba[516:516 + 8] = b"\x12\x34\xAB\xCD"
451+
ba[524:524 + 8] = b"\xFF\xFF\xFF\xFF"
452+
453+
return ba

test/test_mainloop.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import ctypes
2+
import logging
3+
from multiprocessing.context import Process
4+
from os import get_inheritable
5+
import time
6+
import unittest
7+
from unittest import mock
8+
9+
import snap7.error
10+
import snap7.server
11+
import snap7.util
12+
from snap7.util import get_bool, get_dint, get_dword, get_int, get_real, get_sint, get_string, get_usint, get_word
13+
from snap7.client import Client
14+
import snap7.types
15+
16+
logging.basicConfig(level=logging.WARNING)
17+
18+
ip = '127.0.0.1'
19+
tcpport = 1102
20+
db_number = 1
21+
rack = 1
22+
slot = 1
23+
24+
25+
class TestServer(unittest.TestCase):
26+
27+
process = None
28+
29+
@classmethod
30+
def setUpClass(cls):
31+
cls.process = Process(target=snap7.server.mainloop, args=[tcpport, True])
32+
cls.process.start()
33+
time.sleep(2) # wait for server to start
34+
35+
@classmethod
36+
def tearDownClass(cls):
37+
cls.process.terminate()
38+
cls.process.join(1)
39+
if cls.process.is_alive():
40+
cls.process.kill()
41+
42+
def setUp(self):
43+
self.client: Client = snap7.client.Client()
44+
self.client.connect(ip, rack, slot, tcpport)
45+
46+
def tearDown(self):
47+
self.client.disconnect()
48+
self.client.destroy()
49+
50+
@unittest.skip("TODO: only first test used")
51+
def test_read_prefill_db(self):
52+
data = self.client.db_read(0, 0, 7)
53+
boolean = snap7.util.get_bool(data, 0, 0)
54+
print(data)
55+
self.assertEqual(boolean, True)
56+
integer = snap7.util.get_int(data, 1)
57+
self.assertEqual(integer, 128)
58+
real = snap7.util.get_real(data, 3)
59+
self.assertEqual(real, -128)
60+
61+
def test_read_booleans(self):
62+
data = self.client.db_read(0, 0, 1)
63+
self.assertEqual(False, get_bool(data, 0, 0))
64+
self.assertEqual(True, get_bool(data, 0, 1))
65+
self.assertEqual(False, get_bool(data, 0, 2))
66+
self.assertEqual(True, get_bool(data, 0, 3))
67+
self.assertEqual(False, get_bool(data, 0, 4))
68+
self.assertEqual(True, get_bool(data, 0, 5))
69+
self.assertEqual(False, get_bool(data, 0, 6))
70+
self.assertEqual(True, get_bool(data, 0, 7))
71+
72+
def test_read_small_int(self):
73+
data = self.client.db_read(0, 10, 4)
74+
value_1 = get_sint(data, 0)
75+
value_2 = get_sint(data, 1)
76+
value_3 = get_sint(data, 2)
77+
value_4 = get_sint(data, 3)
78+
self.assertEqual(value_1, -128)
79+
self.assertEqual(value_2, 0)
80+
self.assertEqual(value_3, 100)
81+
self.assertEqual(value_4, 127)
82+
83+
def test_read_unsigned_small_int(self):
84+
data = self.client.db_read(0, 20, 2)
85+
self.assertEqual(get_usint(data, 0), 0)
86+
self.assertEqual(get_usint(data, 1), 255)
87+
88+
def test_read_int(self):
89+
data = self.client.db_read(0, 30, 10)
90+
self.assertEqual(get_int(data, 0), -32768)
91+
self.assertEqual(get_int(data, 2), -1234)
92+
self.assertEqual(get_int(data, 4), 0)
93+
self.assertEqual(get_int(data, 6), 1234)
94+
self.assertEqual(get_int(data, 8), 32767)
95+
96+
def test_read_double_int(self):
97+
data = self.client.db_read(0, 40, 4 * 5)
98+
self.assertEqual(get_dint(data, 0), -2147483648)
99+
self.assertEqual(get_dint(data, 4), -32768)
100+
self.assertEqual(get_dint(data, 8), 0)
101+
self.assertEqual(get_dint(data, 12), 32767)
102+
self.assertEqual(get_dint(data, 16), 2147483647)
103+
104+
def test_read_real(self):
105+
data = self.client.db_read(0, 60, 4 * 9)
106+
self.assertAlmostEqual(get_real(data, 0), -3.402823e38, delta=-3.402823e38 * -0.0000001)
107+
self.assertAlmostEqual(get_real(data, 4), -3.402823e12, delta=-3.402823e12 * -0.0000001)
108+
self.assertAlmostEqual(get_real(data, 8), -175494351e-38, delta=-175494351e-38 * -0.0000001)
109+
self.assertAlmostEqual(get_real(data, 12), -1.175494351e-12, delta=-1.175494351e-12 * -0.0000001)
110+
self.assertAlmostEqual(get_real(data, 16), 0.0)
111+
self.assertAlmostEqual(get_real(data, 20), 1.175494351e-38, delta=1.175494351e-38 * 0.0000001)
112+
self.assertAlmostEqual(get_real(data, 24), 1.175494351e-12, delta=1.175494351e-12 * 0.0000001)
113+
self.assertAlmostEqual(get_real(data, 28), 3.402823466e12, delta=3.402823466e12 * 0.0000001)
114+
self.assertAlmostEqual(get_real(data, 32), 3.402823466e38, delta=3.402823466e38 * 0.0000001)
115+
116+
def test_read_string(self):
117+
data = self.client.db_read(0, 100, 254)
118+
self.assertEqual(get_string(data, 0, 254), "the brown fox jumps over the lazy dog")
119+
120+
def test_read_word(self):
121+
data = self.client.db_read(0, 400, 4 * 4)
122+
self.assertEqual(get_word(data, 0), 0x0000)
123+
self.assertEqual(get_word(data, 4), 0x1234)
124+
self.assertEqual(get_word(data, 8), 0xABCD)
125+
self.assertEqual(get_word(data, 12), 0xFFFF)
126+
127+
def test_read_double_word(self):
128+
data = self.client.db_read(0, 500, 8 * 4)
129+
self.assertEqual(get_dword(data, 0), 0x00000000)
130+
self.assertEqual(get_dword(data, 8), 0x12345678)
131+
self.assertEqual(get_dword(data, 16), 0x1234ABCD)
132+
self.assertEqual(get_dword(data, 24), 0xFFFFFFFF)
133+
134+
135+
if __name__ == '__main__':
136+
import logging
137+
138+
logging.basicConfig()
139+
unittest.main()

0 commit comments

Comments
 (0)