# HG changeset patch # User Edouard Tisserant # Date 1733403419 -3600 # Node ID d2f5eb3c7d6edd2f45b8501c901630073f52f31e # Parent 03f007a175b5d15199364bf269d50cf9e3477f1d py_ext: fix CSV Writer fix POU logic : - SAVE is a BOOL - invocation of py_eval on rising edge of SAVE - remove save python argument fix python: - use no encoding for file open (python2) - re-use detected dialect if any - use no "rt+" and truncate since no need to re-sniff dialect for output file - return "OK" instead of "#SUCCESS", preventing POU logic to ACK result - support creating new line if writing just after last line - support appending data on short rows fix example: - use a HMI:Button to trigger CSV write instead of HMI:Input +1 - reload CSVs on on each new CSV opened in file browser - add display of CSV write output diff -r 03f007a175b5 -r d2f5eb3c7d6e exemples/svghmi_csv_json_img_table/plc.xml --- a/exemples/svghmi_csv_json_img_table/plc.xml Wed Dec 04 12:00:37 2024 +0100 +++ b/exemples/svghmi_csv_json_img_table/plc.xml Thu Dec 05 13:56:59 2024 +0100 @@ -1,7 +1,7 @@ - + @@ -43,7 +43,7 @@ - + diff -r 03f007a175b5 -r d2f5eb3c7d6e exemples/svghmi_csv_json_img_table/py_ext_0@py_ext/pyfile.xml --- a/exemples/svghmi_csv_json_img_table/py_ext_0@py_ext/pyfile.xml Wed Dec 04 12:00:37 2024 +0100 +++ b/exemples/svghmi_csv_json_img_table/py_ext_0@py_ext/pyfile.xml Thu Dec 05 13:56:59 2024 +0100 @@ -52,6 +52,7 @@ if action == 'onClick[acknowledge]': if sent_path.endswith('.csv'): PLCGlobals.FileName = sent_path + pyext_csv_reload() else: PLCGlobals.CurrentPath = path = sent_path diff -r 03f007a175b5 -r d2f5eb3c7d6e exemples/svghmi_csv_json_img_table/svghmi_0@svghmi/svghmi.svg --- a/exemples/svghmi_csv_json_img_table/svghmi_0@svghmi/svghmi.svg Wed Dec 04 12:00:37 2024 +0100 +++ b/exemples/svghmi_csv_json_img_table/svghmi_0@svghmi/svghmi.svg Thu Dec 05 13:56:59 2024 +0100 @@ -2,7 +2,7 @@ image/svg+xmlnumber7418529630Esc+/-number + +7 + +4 + +1 + +8 + +5 + +2 + +9 + +6 + +3 + +0 + +Esc + ++/- + +information.information + +. + +Q + +W + +E + +R + +T + +Y + +U + +I + +O + +P + +A + +S + +D + +F + +G + +H + +J + +K + +L + +Z + +X + +C + +V + +B + +N + +M + +. + +: + +; + +, + +1 + +2 + +3 + +4 + +5 + +6 + +7 + +8 + +9 + +- + +0 + ++ + +Esc + +QWERTYUIOPASDFGHJKLZXCVBNM.:;,123456789-0+EscCaps + +Lock + +CapsLockCapsLockCaps + +Lock + +textShiftShiftShiftShifttext + +Shift + +Shift + +Shift + +Shift + +informationinformation + +HMI:TextStylListHMI:TextStylList + +HMI:ListHMI:List + +UpdateWrite + +reset88888888blahHome + +blahblah + +HMI:JsonTablerangepositionfilterHMI:JsonTable + + + + + + + +In this example, JsonTable widget is used as a list of CSV files.In this example, JsonTable widget is used as a file browser to pick a CSV fileJSON data is exchanged with python code in py_ext_0 using HTTP POST + +HMI:JsonTable + +88 + +88 + +88 + + + + + +Row#Row# + +Column#Column# + +Content: + + value + id="tspan1850" + sodipodi:role="line" + style="fill:#00ff00;stroke-width:1">value + +8 + + + + diff -r 03f007a175b5 -r d2f5eb3c7d6e py_ext/pous.xml --- a/py_ext/pous.xml Wed Dec 04 12:00:37 2024 +0100 +++ b/py_ext/pous.xml Thu Dec 05 13:56:59 2024 +0100 @@ -1,7 +1,7 @@ - + @@ -1659,13 +1659,6 @@ - - - - - - - @@ -1701,16 +1694,11 @@ - + - - - - - @@ -1735,15 +1723,15 @@ - - + + - - + + @@ -1751,8 +1739,8 @@ - - + + @@ -1760,8 +1748,8 @@ - - + + @@ -1769,8 +1757,8 @@ - - + + @@ -1778,8 +1766,8 @@ - - + + @@ -1787,283 +1775,113 @@ - - + + - + - - + + - + - - + + - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - + + + + + + + + + + + - - - + + + 'CSVWrInt("' - + FILE_NAME - + + + + + '",' + + + + + + + ROW + + + - '",' - - - + ',' + + + - ROW - - - + COLUMN + + + + + + + ',"' + + + - ',' - - - + CONTENT + + + - COLUMN - - - - - - - ',"' - - - - - - - CONTENT - - - - - - - '",' - - - - - - - SAVE - - - - - - - ')' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OLDCODE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OLDCODE - - - - - - - OLDCODE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + '")' + - + @@ -2075,24 +1893,24 @@ - + - + - - + + - + - - + + @@ -2107,14 +1925,14 @@ - + - - + + @@ -2128,37 +1946,15 @@ - - - - - - - - - - - - - - - - - - - - - - - + - - - + + + @@ -2166,8 +1962,8 @@ - - + + @@ -2187,114 +1983,75 @@ - + - - - - - - pyext_csv_update + + + + + + SAVE - + - - - - + + + + ACK - + - - - - + + + + RESULT - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + @@ -2302,10 +2059,10 @@ - - - - + + + + @@ -2320,14 +2077,14 @@ - + - - + + @@ -2335,10 +2092,8 @@ - - - - + + @@ -2353,30 +2108,30 @@ - + 1 - + '#' - + - - - - + + + + @@ -2391,27 +2146,27 @@ - + - - + + - - - - - - - + + + + + + + @@ -2426,16 +2181,16 @@ - + - - - - + + + + @@ -2443,8 +2198,8 @@ - - + + diff -r 03f007a175b5 -r d2f5eb3c7d6e py_ext/py_ext.py --- a/py_ext/py_ext.py Wed Dec 04 12:00:37 2024 +0100 +++ b/py_ext/py_ext.py Thu Dec 05 13:56:59 2024 +0100 @@ -62,6 +62,8 @@ try: row = data[rowidx] + if not row and rowidx == len(data)-1: + raise IndexError except IndexError: return "#ROW_NOT_FOUND" @@ -116,53 +118,61 @@ return "#COL_NOT_FOUND" -csv_int_files = {} -def CSVWrInt(fname, rowidx, colidx, content, save): +def CSVWrInt(fname, rowidx, colidx, content): \"\"\" Update value at row/column pointed by integer indexes Assumes data starts at first row and first column, no headers. \"\"\" - if save > 0: - global csv_int_files - data = csv_int_files.get(fname, None) - if data is None: - data = list() - try: - csvfile = open(fname, 'rt', encoding='utf-8') - except IOError: - return "#FILE_NOT_FOUND" - try: - dialect = csv.Sniffer().sniff(csvfile.read(1024)) - csvfile.seek(0) - reader = csv.reader(csvfile, dialect) - for row in reader: - data.append(row) - except csv.Error as e: - return "#CSV_ERROR" - finally: - csvfile.close() - csv_int_files[fname] = data - - try: + + global csv_int_files + dialect = None + data = csv_int_files.get(fname, None) + if data is None: + data = list() + try: + csvfile = open(fname, 'rt', encoding='utf-8') + except IOError: + return "#FILE_NOT_FOUND" + try: + dialect = csv.Sniffer().sniff(csvfile.read(1024)) + csvfile.seek(0) + reader = csv.reader(csvfile, dialect) + for row in reader: + data.append(row) + except csv.Error as e: + return "#CSV_ERROR" + finally: + csvfile.close() + csv_int_files[fname] = data + + try: + if rowidx == len(data): + row = [] + data.append(row) + else: row = data[rowidx] - except IndexError: - return "#ROW_NOT_FOUND" - - try: + except IndexError: + return "#ROW_NOT_FOUND" + + try: + if rowidx > 0 and colidx >= len(data[0]): + raise IndexError + if colidx >= len(row): + row.extend([""] * (colidx - len(row)) + [content]) + else: row[colidx] = content - except IndexError: - return "#COL_NOT_FOUND" - - wfile = open(fname, 'rt+', encoding='utf-8') - wdialect = csv.Sniffer().sniff(wfile.read(1024)) - wfile.seek(0) - writer = csv.writer(wfile, wdialect) + except IndexError: + return "#COL_NOT_FOUND" + + try: + wfile = open(fname, 'wt') + writer = csv.writer(wfile) if not(dialect) else csv.writer(wfile, dialect) for row in data: writer.writerow(row) - wfile.truncate() + finally: wfile.close() - - return "#SUCCESS" + + return "OK" def pyext_csv_reload(): @@ -210,4 +220,4 @@ class PythonFile(PythonFileCTNMixin): def GetIconName(self): - return "Pyfile" \ No newline at end of file + return "Pyfile"