diff --git a/checker_gen1.uvoptx b/checker_gen1.uvoptx index 4857ea5..2d73e36 100644 --- a/checker_gen1.uvoptx +++ b/checker_gen1.uvoptx @@ -120,7 +120,7 @@ 0 DLGUARM - 7 + d 0 @@ -157,16 +157,16 @@ 1 - 1 - 0x100004D8 + 0 + errbit 0 2 - 1 - m + 0 + src_data 0 @@ -1428,7 +1428,7 @@ lwip - 0 + 1 0 0 0 diff --git a/python/checker_save.py b/python/checker_save.py index a3f85a0..bef91b9 100644 --- a/python/checker_save.py +++ b/python/checker_save.py @@ -1,5 +1,6 @@ - +import lupa +from lupa import LuaRuntime class save: @@ -40,5 +41,17 @@ class save: if __name__ == "__main__": sa=save() print(sa.hex2bit(bytes([0xab,0xcd]))) - + lua = LuaRuntime(unpack_returned_tuples=True) + save_json=lua.eval("function(a) json=a end") + with open("file/json.lua",encoding="utf-8") as f: + save_json(lua.execute(f.read())) + save_prints=lua.eval("function(a) prints=a end") + with open("file/prints.lua",encoding="utf-8") as f: + save_prints(lua.execute(f.read())) + lua.execute("cfg_name=\"file/checker_ye_cfg.json\"\n") + lua.execute("check_data={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}\n") + with open("file/judge.lua",encoding="utf-8") as f: + lua.execute(f.read()) + # lua_func = lua.eval('function(a, b) return a+b end') + # print(lua_func(1,2)) diff --git a/python/checker_test.py b/python/checker_test.py index 937b891..c6ed527 100644 --- a/python/checker_test.py +++ b/python/checker_test.py @@ -173,5 +173,5 @@ class port: if __name__ == "__main__": p=port() # 批检仪测试 - p.open("utcp",0) + p.open("com5",115200) p.checker_test(5) diff --git a/python/file/json.lua b/python/file/json.lua new file mode 100644 index 0000000..6415dfb --- /dev/null +++ b/python/file/json.lua @@ -0,0 +1,1869 @@ +-- -*- coding: utf-8 -*- +-- +-- Simple JSON encoding and decoding in pure Lua. +-- +-- Copyright 2010-2017 Jeffrey Friedl +-- http://regex.info/blog/ +-- Latest version: http://regex.info/blog/lua/json +-- +-- This code is released under a Creative Commons CC-BY "Attribution" License: +-- http://creativecommons.org/licenses/by/3.0/deed.en_US +-- +-- It can be used for any purpose so long as: +-- 1) the copyright notice above is maintained +-- 2) the web-page links above are maintained +-- 3) the 'AUTHOR_NOTE' string below is maintained +-- +local VERSION = '20211016.28' -- version history at end of file +local AUTHOR_NOTE = "-[ JSON.lua package by Jeffrey Friedl (http://regex.info/blog/lua/json) version 20211016.28 ]-" + +-- +-- The 'AUTHOR_NOTE' variable exists so that information about the source +-- of the package is maintained even in compiled versions. It's also +-- included in OBJDEF below mostly to quiet warnings about unused variables. +-- +local OBJDEF = { + VERSION = VERSION, + AUTHOR_NOTE = AUTHOR_NOTE, +} + + +-- +-- Simple JSON encoding and decoding in pure Lua. +-- JSON definition: http://www.json.org/ +-- +-- +-- JSON = assert(loadfile "JSON.lua")() -- one-time load of the routines +-- +-- local lua_value = JSON:decode(raw_json_text) +-- +-- local raw_json_text = JSON:encode(lua_table_or_value) +-- local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- "pretty printed" version for human readability +-- +-- +-- +-- DECODING (from a JSON string to a Lua table) +-- +-- +-- JSON = assert(loadfile "JSON.lua")() -- one-time load of the routines +-- +-- local lua_value = JSON:decode(raw_json_text) +-- +-- If the JSON text is for an object or an array, e.g. +-- { "what": "books", "count": 3 } +-- or +-- [ "Larry", "Curly", "Moe" ] +-- +-- the result is a Lua table, e.g. +-- { what = "books", count = 3 } +-- or +-- { "Larry", "Curly", "Moe" } +-- +-- +-- The encode and decode routines accept an optional second argument, +-- "etc", which is not used during encoding or decoding, but upon error +-- is passed along to error handlers. It can be of any type (including nil). +-- +-- +-- +-- ERROR HANDLING DURING DECODE +-- +-- With most errors during decoding, this code calls +-- +-- JSON:onDecodeError(message, text, location, etc) +-- +-- with a message about the error, and if known, the JSON text being +-- parsed and the byte count where the problem was discovered. You can +-- replace the default JSON:onDecodeError() with your own function. +-- +-- The default onDecodeError() merely augments the message with data +-- about the text and the location (and, an 'etc' argument had been +-- provided to decode(), its value is tacked onto the message as well), +-- and then calls JSON.assert(), which itself defaults to Lua's built-in +-- assert(), and can also be overridden. +-- +-- For example, in an Adobe Lightroom plugin, you might use something like +-- +-- function JSON:onDecodeError(message, text, location, etc) +-- LrErrors.throwUserError("Internal Error: invalid JSON data") +-- end +-- +-- or even just +-- +-- function JSON.assert(message) +-- LrErrors.throwUserError("Internal Error: " .. message) +-- end +-- +-- If JSON:decode() is passed a nil, this is called instead: +-- +-- JSON:onDecodeOfNilError(message, nil, nil, etc) +-- +-- and if JSON:decode() is passed HTML instead of JSON, this is called: +-- +-- JSON:onDecodeOfHTMLError(message, text, nil, etc) +-- +-- The use of the 'etc' argument allows stronger coordination between +-- decoding and error reporting, especially when you provide your own +-- error-handling routines. Continuing with the the Adobe Lightroom +-- plugin example: +-- +-- function JSON:onDecodeError(message, text, location, etc) +-- local note = "Internal Error: invalid JSON data" +-- if type(etc) = 'table' and etc.photo then +-- note = note .. " while processing for " .. etc.photo:getFormattedMetadata('fileName') +-- end +-- LrErrors.throwUserError(note) +-- end +-- +-- : +-- : +-- +-- for i, photo in ipairs(photosToProcess) do +-- : +-- : +-- local data = JSON:decode(someJsonText, { photo = photo }) +-- : +-- : +-- end +-- +-- +-- +-- If the JSON text passed to decode() has trailing garbage (e.g. as with the JSON "[123]xyzzy"), +-- the method +-- +-- JSON:onTrailingGarbage(json_text, location, parsed_value, etc) +-- +-- is invoked, where: +-- +-- 'json_text' is the original JSON text being parsed, +-- 'location' is the count of bytes into 'json_text' where the garbage starts (6 in the example), +-- 'parsed_value' is the Lua result of what was successfully parsed ({123} in the example), +-- 'etc' is as above. +-- +-- If JSON:onTrailingGarbage() does not abort, it should return the value decode() should return, +-- or nil + an error message. +-- +-- local new_value, error_message = JSON:onTrailingGarbage() +-- +-- The default JSON:onTrailingGarbage() simply invokes JSON:onDecodeError("trailing garbage"...), +-- but you can have this package ignore trailing garbage via +-- +-- function JSON:onTrailingGarbage(json_text, location, parsed_value, etc) +-- return parsed_value +-- end +-- +-- +-- DECODING AND STRICT TYPES +-- +-- Because both JSON objects and JSON arrays are converted to Lua tables, +-- it's not normally possible to tell which original JSON type a +-- particular Lua table was derived from, or guarantee decode-encode +-- round-trip equivalency. +-- +-- However, if you enable strictTypes, e.g. +-- +-- JSON = assert(loadfile "JSON.lua")() --load the routines +-- JSON.strictTypes = true +-- +-- then the Lua table resulting from the decoding of a JSON object or +-- JSON array is marked via Lua metatable, so that when re-encoded with +-- JSON:encode() it ends up as the appropriate JSON type. +-- +-- (This is not the default because other routines may not work well with +-- tables that have a metatable set, for example, Lightroom API calls.) +-- +-- +-- DECODING AND STRICT PARSING +-- +-- If strictParsing is true in your JSON object, or if you set strictParsing as a decode option, +-- some kinds of technically-invalid JSON that would normally be accepted are rejected with an error. +-- +-- For example, passing in an empty string +-- +-- JSON:decode("") +-- +-- normally succeeds with a return value of nil, but +-- +-- JSON:decode("", nil, { strictParsing = true }) +-- +-- results in an error being raised (onDecodeError is called). +-- +-- JSON.strictParsing = true +-- JSON:decode("") +-- +-- achieves the same thing. +-- +-- +-- +-- ENCODING (from a lua table to a JSON string) +-- +-- JSON = assert(loadfile "JSON.lua")() -- one-time load of the routines +-- +-- local raw_json_text = JSON:encode(lua_table_or_value) +-- local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- "pretty printed" version for human readability +-- local custom_pretty = JSON:encode(lua_table_or_value, etc, { pretty = true, indent = "| ", align_keys = false }) +-- +-- On error during encoding, this code calls: +-- +-- JSON:onEncodeError(message, etc) +-- +-- which you can override in your local JSON object. Also see "HANDLING UNSUPPORTED VALUE TYPES" below. +-- +-- The 'etc' in the error call is the second argument to encode() and encode_pretty(), or nil if it wasn't provided. +-- +-- +-- +-- +-- ENCODING OPTIONS +-- +-- An optional third argument, a table of options, can be provided to encode(). +-- +-- encode_options = { +-- -- options for making "pretty" human-readable JSON (see "PRETTY-PRINTING" below) +-- pretty = true, -- turn pretty formatting on +-- indent = " ", -- use this indent for each level of an array/object +-- align_keys = false, -- if true, align the keys in a way that sounds like it should be nice, but is actually ugly +-- array_newline = false, -- if true, array elements become one to a line rather than inline +-- +-- -- other output-related options +-- null = "\0", -- see "ENCODING JSON NULL VALUES" below +-- stringsAreUtf8 = false, -- see "HANDLING UNICODE LINE AND PARAGRAPH SEPARATORS FOR JAVA" below +-- } +-- +-- json_string = JSON:encode(mytable, etc, encode_options) +-- +-- +-- +-- For reference, the defaults are: +-- +-- pretty = false +-- null = nil, +-- stringsAreUtf8 = false, +-- +-- +-- +-- PRETTY-PRINTING +-- +-- Enabling the 'pretty' encode option helps generate human-readable JSON. +-- +-- pretty = JSON:encode(val, etc, { +-- pretty = true, +-- indent = " ", +-- align_keys = false, +-- }) +-- +-- encode_pretty() is also provided: it's identical to encode() except +-- that encode_pretty() provides a default options table if none given in the call: +-- +-- { pretty = true, indent = " ", align_keys = false, array_newline = false } +-- +-- For example, if +-- +-- JSON:encode(data) +-- +-- produces: +-- +-- {"city":"Kyoto","climate":{"avg_temp":16,"humidity":"high","snowfall":"minimal"},"country":"Japan","wards":11} +-- +-- then +-- +-- JSON:encode_pretty(data) +-- +-- produces: +-- +-- { +-- "city": "Kyoto", +-- "climate": { +-- "avg_temp": 16, +-- "humidity": "high", +-- "snowfall": "minimal" +-- }, +-- "country": "Japan", +-- "wards": 11 +-- } +-- +-- The following lines all return identical strings: +-- JSON:encode_pretty(data) +-- JSON:encode_pretty(data, nil, { pretty = true, indent = " ", align_keys = false, array_newline = false}) +-- JSON:encode_pretty(data, nil, { pretty = true, indent = " " }) +-- JSON:encode (data, nil, { pretty = true, indent = " " }) +-- +-- An example of setting your own indent string: +-- +-- JSON:encode_pretty(data, nil, { pretty = true, indent = "| " }) +-- +-- produces: +-- +-- { +-- | "city": "Kyoto", +-- | "climate": { +-- | | "avg_temp": 16, +-- | | "humidity": "high", +-- | | "snowfall": "minimal" +-- | }, +-- | "country": "Japan", +-- | "wards": 11 +-- } +-- +-- An example of setting align_keys to true: +-- +-- JSON:encode_pretty(data, nil, { pretty = true, indent = " ", align_keys = true }) +-- +-- produces: +-- +-- { +-- "city": "Kyoto", +-- "climate": { +-- "avg_temp": 16, +-- "humidity": "high", +-- "snowfall": "minimal" +-- }, +-- "country": "Japan", +-- "wards": 11 +-- } +-- +-- which I must admit is kinda ugly, sorry. This was the default for +-- encode_pretty() prior to version 20141223.14. +-- +-- +-- HANDLING UNICODE LINE AND PARAGRAPH SEPARATORS FOR JAVA +-- +-- If the 'stringsAreUtf8' encode option is set to true, consider Lua strings not as a sequence of bytes, +-- but as a sequence of UTF-8 characters. +-- +-- Currently, the only practical effect of setting this option is that Unicode LINE and PARAGRAPH +-- separators, if found in a string, are encoded with a JSON escape instead of being dumped as is. +-- The JSON is valid either way, but encoding this way, apparently, allows the resulting JSON +-- to also be valid Java. +-- +-- AMBIGUOUS SITUATIONS DURING THE ENCODING +-- +-- During the encode, if a Lua table being encoded contains both string +-- and numeric keys, it fits neither JSON's idea of an object, nor its +-- idea of an array. To get around this, when any string key exists (or +-- when non-positive numeric keys exist), numeric keys are converted to +-- strings. +-- +-- For example, +-- JSON:encode({ "one", "two", "three", SOMESTRING = "some string" })) +-- produces the JSON object +-- {"1":"one","2":"two","3":"three","SOMESTRING":"some string"} +-- +-- To prohibit this conversion and instead make it an error condition, set +-- JSON.noKeyConversion = true +-- +-- +-- ENCODING JSON NULL VALUES +-- +-- Lua tables completely omit keys whose value is nil, so without special handling there's +-- no way to represent JSON object's null value in a Lua table. For example +-- JSON:encode({ username = "admin", password = nil }) +-- +-- produces: +-- +-- {"username":"admin"} +-- +-- In order to actually produce +-- +-- {"username":"admin", "password":null} +-- + +-- one can include a string value for a "null" field in the options table passed to encode().... +-- any Lua table entry with that value becomes null in the JSON output: +-- +-- JSON:encode({ username = "admin", password = "xyzzy" }, -- First arg is the Lua table to encode as JSON. +-- nil, -- Second arg is the 'etc' value, ignored here +-- { null = "xyzzy" }) -- Third arg is th options table +-- +-- produces: +-- +-- {"username":"admin", "password":null} +-- +-- Just be sure to use a string that is otherwise unlikely to appear in your data. +-- The string "\0" (a string with one null byte) may well be appropriate for many applications. +-- +-- The "null" options also applies to Lua tables that become JSON arrays. +-- JSON:encode({ "one", "two", nil, nil }) +-- +-- produces +-- +-- ["one","two"] +-- +-- while +-- +-- NullPlaceholder = "\0" +-- encode_options = { null = NullPlaceholder } +-- JSON:encode({ "one", "two", NullPlaceholder, NullPlaceholder}, nil, encode_options) +-- produces +-- +-- ["one","two",null,null] +-- +-- +-- +-- HANDLING LARGE AND/OR PRECISE NUMBERS +-- +-- +-- Without special handling, numbers in JSON can lose precision in Lua. +-- For example: +-- +-- T = JSON:decode('{ "small":12345, "big":12345678901234567890123456789, "precise":9876.67890123456789012345 }') +-- +-- print("small: ", type(T.small), T.small) +-- print("big: ", type(T.big), T.big) +-- print("precise: ", type(T.precise), T.precise) +-- +-- produces +-- +-- small: number 12345 +-- big: number 1.2345678901235e+28 +-- precise: number 9876.6789012346 +-- +-- Precision is lost with both 'big' and 'precise'. +-- +-- This package offers ways to try to handle this better (for some definitions of "better")... +-- +-- The most precise method is by setting the global: +-- +-- JSON.decodeNumbersAsObjects = true +-- +-- When this is set, numeric JSON data is encoded into Lua in a form that preserves the exact +-- JSON numeric presentation when re-encoded back out to JSON, or accessed in Lua as a string. +-- +-- This is done by encoding the numeric data with a Lua table/metatable that returns +-- the possibly-imprecise numeric form when accessed numerically, but the original precise +-- representation when accessed as a string. +-- +-- Consider the example above, with this option turned on: +-- +-- JSON.decodeNumbersAsObjects = true +-- +-- T = JSON:decode('{ "small":12345, "big":12345678901234567890123456789, "precise":9876.67890123456789012345 }') +-- +-- print("small: ", type(T.small), T.small) +-- print("big: ", type(T.big), T.big) +-- print("precise: ", type(T.precise), T.precise) +-- +-- This now produces: +-- +-- small: table 12345 +-- big: table 12345678901234567890123456789 +-- precise: table 9876.67890123456789012345 +-- +-- However, within Lua you can still use the values (e.g. T.precise in the example above) in numeric +-- contexts. In such cases you'll get the possibly-imprecise numeric version, but in string contexts +-- and when the data finds its way to this package's encode() function, the original full-precision +-- representation is used. +-- +-- You can force access to the string or numeric version via +-- JSON:forceString() +-- JSON:forceNumber() +-- For example, +-- local probably_okay = JSON:forceNumber(T.small) -- 'probably_okay' is a number +-- +-- Code the inspects the JSON-turned-Lua data using type() can run into troubles because what used to +-- be a number can now be a table (e.g. as the small/big/precise example above shows). Update these +-- situations to use JSON:isNumber(item), which returns nil if the item is neither a number nor one +-- of these number objects. If it is either, it returns the number itself. For completeness there's +-- also JSON:isString(item). +-- +-- If you want to try to avoid the hassles of this "number as an object" kludge for all but really +-- big numbers, you can set JSON.decodeNumbersAsObjects and then also set one or both of +-- JSON:decodeIntegerObjectificationLength +-- JSON:decodeDecimalObjectificationLength +-- They refer to the length of the part of the number before and after a decimal point. If they are +-- set and their part is at least that number of digits, objectification occurs. If both are set, +-- objectification occurs when either length is met. +-- +-- ----------------------- +-- +-- Even without using the JSON.decodeNumbersAsObjects option, you can encode numbers in your Lua +-- table that retain high precision upon encoding to JSON, by using the JSON:asNumber() function: +-- +-- T = { +-- imprecise = 123456789123456789.123456789123456789, +-- precise = JSON:asNumber("123456789123456789.123456789123456789") +-- } +-- +-- print(JSON:encode_pretty(T)) +-- +-- This produces: +-- +-- { +-- "precise": 123456789123456789.123456789123456789, +-- "imprecise": 1.2345678912346e+17 +-- } +-- +-- +-- ----------------------- +-- +-- A different way to handle big/precise JSON numbers is to have decode() merely return the exact +-- string representation of the number instead of the number itself. This approach might be useful +-- when the numbers are merely some kind of opaque object identifier and you want to work with them +-- in Lua as strings anyway. +-- +-- This approach is enabled by setting +-- +-- JSON.decodeIntegerStringificationLength = 10 +-- +-- The value is the number of digits (of the integer part of the number) at which to stringify numbers. +-- NOTE: this setting is ignored if JSON.decodeNumbersAsObjects is true, as that takes precedence. +-- +-- Consider our previous example with this option set to 10: +-- +-- JSON.decodeIntegerStringificationLength = 10 +-- +-- T = JSON:decode('{ "small":12345, "big":12345678901234567890123456789, "precise":9876.67890123456789012345 }') +-- +-- print("small: ", type(T.small), T.small) +-- print("big: ", type(T.big), T.big) +-- print("precise: ", type(T.precise), T.precise) +-- +-- This produces: +-- +-- small: number 12345 +-- big: string 12345678901234567890123456789 +-- precise: number 9876.6789012346 +-- +-- The long integer of the 'big' field is at least JSON.decodeIntegerStringificationLength digits +-- in length, so it's converted not to a Lua integer but to a Lua string. Using a value of 0 or 1 ensures +-- that all JSON numeric data becomes strings in Lua. +-- +-- Note that unlike +-- JSON.decodeNumbersAsObjects = true +-- this stringification is simple and unintelligent: the JSON number simply becomes a Lua string, and that's the end of it. +-- If the string is then converted back to JSON, it's still a string. After running the code above, adding +-- print(JSON:encode(T)) +-- produces +-- {"big":"12345678901234567890123456789","precise":9876.6789012346,"small":12345} +-- which is unlikely to be desired. +-- +-- There's a comparable option for the length of the decimal part of a number: +-- +-- JSON.decodeDecimalStringificationLength +-- +-- This can be used alone or in conjunction with +-- +-- JSON.decodeIntegerStringificationLength +-- +-- to trip stringification on precise numbers with at least JSON.decodeIntegerStringificationLength digits after +-- the decimal point. (Both are ignored if JSON.decodeNumbersAsObjects is true.) +-- +-- This example: +-- +-- JSON.decodeIntegerStringificationLength = 10 +-- JSON.decodeDecimalStringificationLength = 5 +-- +-- T = JSON:decode('{ "small":12345, "big":12345678901234567890123456789, "precise":9876.67890123456789012345 }') +-- +-- print("small: ", type(T.small), T.small) +-- print("big: ", type(T.big), T.big) +-- print("precise: ", type(T.precise), T.precise) +-- +-- produces: +-- +-- small: number 12345 +-- big: string 12345678901234567890123456789 +-- precise: string 9876.67890123456789012345 +-- +-- +-- HANDLING UNSUPPORTED VALUE TYPES +-- +-- Among the encoding errors that might be raised is an attempt to convert a table value that has a type +-- that this package hasn't accounted for: a function, userdata, or a thread. You can handle these types as table +-- values (but not as table keys) if you supply a JSON:unsupportedTypeEncoder() method along the lines of the +-- following example: +-- +-- function JSON:unsupportedTypeEncoder(value_of_unsupported_type) +-- if type(value_of_unsupported_type) == 'function' then +-- return "a function value" +-- else +-- return nil +-- end +-- end +-- +-- Your unsupportedTypeEncoder() method is actually called with a bunch of arguments: +-- +-- self:unsupportedTypeEncoder(value, parents, etc, options, indent, for_key) +-- +-- The 'value' is the function, thread, or userdata to be converted to JSON. +-- +-- The 'etc' and 'options' arguments are those passed to the original encode(). The other arguments are +-- probably of little interest; see the source code. (Note that 'for_key' is never true, as this function +-- is invoked only on table values; table keys of these types still trigger the onEncodeError method.) +-- +-- If your unsupportedTypeEncoder() method returns a string, it's inserted into the JSON as is. +-- If it returns nil plus an error message, that error message is passed through to an onEncodeError invocation. +-- If it returns only nil, processing falls through to a default onEncodeError invocation. +-- +-- If you want to handle everything in a simple way: +-- +-- function JSON:unsupportedTypeEncoder(value) +-- return tostring(value) +-- end +-- +-- +-- SUMMARY OF METHODS YOU CAN OVERRIDE IN YOUR LOCAL LUA JSON OBJECT +-- +-- assert +-- onDecodeError +-- onDecodeOfNilError +-- onDecodeOfHTMLError +-- onTrailingGarbage +-- onEncodeError +-- unsupportedTypeEncoder +-- +-- If you want to create a separate Lua JSON object with its own error handlers, +-- you can reload JSON.lua or use the :new() method. +-- +--------------------------------------------------------------------------- + +local default_pretty_indent = " " +local default_pretty_options = { pretty = true, indent = default_pretty_indent, align_keys = false, array_newline = false } + +local isArray = { __tostring = function() return "JSON array" end } isArray.__index = isArray +local isObject = { __tostring = function() return "JSON object" end } isObject.__index = isObject + +function OBJDEF:newArray(tbl) + return setmetatable(tbl or {}, isArray) +end + +function OBJDEF:newObject(tbl) + return setmetatable(tbl or {}, isObject) +end + + + + +local function getnum(op) + return type(op) == 'number' and op or op.N +end + +local isNumber = { + __tostring = function(T) return T.S end, + __unm = function(op) return getnum(op) end, + + __concat = function(op1, op2) return tostring(op1) .. tostring(op2) end, + __add = function(op1, op2) return getnum(op1) + getnum(op2) end, + __sub = function(op1, op2) return getnum(op1) - getnum(op2) end, + __mul = function(op1, op2) return getnum(op1) * getnum(op2) end, + __div = function(op1, op2) return getnum(op1) / getnum(op2) end, + __mod = function(op1, op2) return getnum(op1) % getnum(op2) end, + __pow = function(op1, op2) return getnum(op1) ^ getnum(op2) end, + __lt = function(op1, op2) return getnum(op1) < getnum(op2) end, + __eq = function(op1, op2) return getnum(op1) == getnum(op2) end, + __le = function(op1, op2) return getnum(op1) <= getnum(op2) end, +} +isNumber.__index = isNumber + +function OBJDEF:asNumber(item) + + if getmetatable(item) == isNumber then + -- it's already a JSON number object. + return item + elseif type(item) == 'table' and type(item.S) == 'string' and type(item.N) == 'number' then + -- it's a number-object table that lost its metatable, so give it one + return setmetatable(item, isNumber) + else + -- the normal situation... given a number or a string representation of a number.... + local holder = { + S = tostring(item), -- S is the representation of the number as a string, which remains precise + N = tonumber(item), -- N is the number as a Lua number. + } + return setmetatable(holder, isNumber) + end +end + +-- +-- Given an item that might be a normal string or number, or might be an 'isNumber' object defined above, +-- return the string version. This shouldn't be needed often because the 'isNumber' object should autoconvert +-- to a string in most cases, but it's here to allow it to be forced when needed. +-- +function OBJDEF:forceString(item) + if type(item) == 'table' and type(item.S) == 'string' then + return item.S + else + return tostring(item) + end +end + +-- +-- Given an item that might be a normal string or number, or might be an 'isNumber' object defined above, +-- return the numeric version. +-- +function OBJDEF:forceNumber(item) + if type(item) == 'table' and type(item.N) == 'number' then + return item.N + else + return tonumber(item) + end +end + +-- +-- If the given item is a number, return it. Otherwise, return nil. +-- This, this can be used both in a conditional and to access the number when you're not sure its form. +-- +function OBJDEF:isNumber(item) + if type(item) == 'number' then + return item + elseif type(item) == 'table' and type(item.N) == 'number' then + return item.N + else + return nil + end +end + +function OBJDEF:isString(item) + if type(item) == 'string' then + return item + elseif type(item) == 'table' and type(item.S) == 'string' then + return item.S + else + return nil + end +end + + + + +-- +-- Some utf8 routines to deal with the fact that Lua handles only bytes +-- +local function top_three_bits(val) + return math.floor(val / 0x20) +end + +local function top_four_bits(val) + return math.floor(val / 0x10) +end + +local function unicode_character_bytecount_based_on_first_byte(first_byte) + local W = string.byte(first_byte) + if W < 0x80 then + return 1 + elseif (W == 0xC0) or (W == 0xC1) or (W >= 0x80 and W <= 0xBF) or (W >= 0xF5) then + -- this is an error -- W can't be the start of a utf8 character + return 0 + elseif top_three_bits(W) == 0x06 then + return 2 + elseif top_four_bits(W) == 0x0E then + return 3 + else + return 4 + end +end + + + +local function unicode_codepoint_as_utf8(codepoint) + -- + -- codepoint is a number + -- + if codepoint <= 127 then + return string.char(codepoint) + + elseif codepoint <= 2047 then + -- + -- 110yyyxx 10xxxxxx <-- useful notation from http://en.wikipedia.org/wiki/Utf8 + -- + local highpart = math.floor(codepoint / 0x40) + local lowpart = codepoint - (0x40 * highpart) + return string.char(0xC0 + highpart, + 0x80 + lowpart) + + elseif codepoint <= 65535 then + -- + -- 1110yyyy 10yyyyxx 10xxxxxx + -- + local highpart = math.floor(codepoint / 0x1000) + local remainder = codepoint - 0x1000 * highpart + local midpart = math.floor(remainder / 0x40) + local lowpart = remainder - 0x40 * midpart + + highpart = 0xE0 + highpart + midpart = 0x80 + midpart + lowpart = 0x80 + lowpart + + -- + -- Check for an invalid character (thanks Andy R. at Adobe). + -- See table 3.7, page 93, in http://www.unicode.org/versions/Unicode5.2.0/ch03.pdf#G28070 + -- + if ( highpart == 0xE0 and midpart < 0xA0 ) or + ( highpart == 0xED and midpart > 0x9F ) or + ( highpart == 0xF0 and midpart < 0x90 ) or + ( highpart == 0xF4 and midpart > 0x8F ) + then + return "?" + else + return string.char(highpart, + midpart, + lowpart) + end + + else + -- + -- 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx + -- + local highpart = math.floor(codepoint / 0x40000) + local remainder = codepoint - 0x40000 * highpart + local midA = math.floor(remainder / 0x1000) + remainder = remainder - 0x1000 * midA + local midB = math.floor(remainder / 0x40) + local lowpart = remainder - 0x40 * midB + + return string.char(0xF0 + highpart, + 0x80 + midA, + 0x80 + midB, + 0x80 + lowpart) + end +end + +function OBJDEF:onDecodeError(message, text, location, etc) + if text then + if location then + message = string.format("%s at byte %d of: %s", message, location, text) + else + message = string.format("%s: %s", message, text) + end + end + + if etc ~= nil then + message = message .. " (" .. OBJDEF:encode(etc) .. ")" + end + + if self.assert then + self.assert(false, message) + else + assert(false, message) + end +end + +function OBJDEF:onTrailingGarbage(json_text, location, parsed_value, etc) + return self:onDecodeError("trailing garbage", json_text, location, etc) +end + +OBJDEF.onDecodeOfNilError = OBJDEF.onDecodeError +OBJDEF.onDecodeOfHTMLError = OBJDEF.onDecodeError + +function OBJDEF:onEncodeError(message, etc) + if etc ~= nil then + message = message .. " (" .. OBJDEF:encode(etc) .. ")" + end + + if self.assert then + self.assert(false, message) + else + assert(false, message) + end +end + +local function grok_number(self, text, start, options) + -- + -- Grab the integer part + -- + local integer_part = text:match('^-?[1-9]%d*', start) + or text:match("^-?0", start) + + if not integer_part then + self:onDecodeError("expected number", text, start, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible + end + + local i = start + integer_part:len() + + -- + -- Grab an optional decimal part + -- + local decimal_part = text:match('^%.%d+', i) or "" + + i = i + decimal_part:len() + + -- + -- Grab an optional exponential part + -- + local exponent_part = text:match('^[eE][-+]?%d+', i) or "" + + i = i + exponent_part:len() + + local full_number_text = integer_part .. decimal_part .. exponent_part + + if options.decodeNumbersAsObjects then + + local objectify = false + + if not options.decodeIntegerObjectificationLength and not options.decodeDecimalObjectificationLength then + -- no options, so objectify + objectify = true + + elseif (options.decodeIntegerObjectificationLength + and + (integer_part:len() >= options.decodeIntegerObjectificationLength or exponent_part:len() > 0)) + + or + (options.decodeDecimalObjectificationLength + and + (decimal_part:len() >= options.decodeDecimalObjectificationLength or exponent_part:len() > 0)) + then + -- have options and they are triggered, so objectify + objectify = true + end + + if objectify then + return OBJDEF:asNumber(full_number_text), i + end + -- else, fall through to try to return as a straight-up number + + else + + -- Not always decoding numbers as objects, so perhaps encode as strings? + + -- + -- If we're told to stringify only under certain conditions, so do. + -- We punt a bit when there's an exponent by just stringifying no matter what. + -- I suppose we should really look to see whether the exponent is actually big enough one + -- way or the other to trip stringification, but I'll be lazy about it until someone asks. + -- + if (options.decodeIntegerStringificationLength + and + (integer_part:len() >= options.decodeIntegerStringificationLength or exponent_part:len() > 0)) + + or + + (options.decodeDecimalStringificationLength + and + (decimal_part:len() >= options.decodeDecimalStringificationLength or exponent_part:len() > 0)) + then + return full_number_text, i -- this returns the exact string representation seen in the original JSON + end + + end + + + local as_number = tonumber(full_number_text) + + if not as_number then + self:onDecodeError("bad number", text, start, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible + end + + return as_number, i +end + + +local backslash_escape_conversion = { + ['"'] = '"', + ['/'] = "/", + ['\\'] = "\\", + ['b'] = "\b", + ['f'] = "\f", + ['n'] = "\n", + ['r'] = "\r", + ['t'] = "\t", +} + +local function grok_string(self, text, start, options) + + if text:sub(start,start) ~= '"' then + self:onDecodeError("expected string's opening quote", text, start, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible + end + + local i = start + 1 -- +1 to bypass the initial quote + local text_len = text:len() + local VALUE = "" + while i <= text_len do + local c = text:sub(i,i) + if c == '"' then + return VALUE, i + 1 + end + if c ~= '\\' then + + -- should grab the next bytes as per the number of bytes for this utf8 character + local byte_count = unicode_character_bytecount_based_on_first_byte(c) + + local next_character + if byte_count == 0 then + self:onDecodeError("non-utf8 sequence", text, i, options.etc) + elseif byte_count == 1 then + if options.strictParsing and string.byte(c) < 0x20 then + self:onDecodeError("Unescaped control character", text, i+1, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible + end + next_character = c + elseif byte_count == 2 then + next_character = text:match('^(.[\128-\191])', i) + elseif byte_count == 3 then + next_character = text:match('^(.[\128-\191][\128-\191])', i) + elseif byte_count == 4 then + next_character = text:match('^(.[\128-\191][\128-\191][\128-\191])', i) + end + + if not next_character then + self:onDecodeError("incomplete utf8 sequence", text, i, options.etc) + return nil, i -- in case the error method doesn't abort, return something sensible + end + + + VALUE = VALUE .. next_character + i = i + byte_count + + else + -- + -- We have a backslash escape + -- + i = i + 1 + + local next_byte = text:match('^(.)', i) + + if next_byte == nil then + -- string ended after the \ + self:onDecodeError("unfinished \\ escape", text, i, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible + end + + if backslash_escape_conversion[next_byte] then + VALUE = VALUE .. backslash_escape_conversion[next_byte] + i = i + 1 + else + -- + -- The only other valid use of \ that remains is in the form of \u#### + -- + + local hex = text:match('^u([0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', i) + if hex then + i = i + 5 -- bypass what we just read + + -- We have a Unicode codepoint. It could be standalone, or if in the proper range and + -- followed by another in a specific range, it'll be a two-code surrogate pair. + local codepoint = tonumber(hex, 16) + if codepoint >= 0xD800 and codepoint <= 0xDBFF then + -- it's a hi surrogate... see whether we have a following low + local lo_surrogate = text:match('^\\u([dD][cdefCDEF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', i) + if lo_surrogate then + i = i + 6 -- bypass the low surrogate we just read + codepoint = 0x2400 + (codepoint - 0xD800) * 0x400 + tonumber(lo_surrogate, 16) + else + -- not a proper low, so we'll just leave the first codepoint as is and spit it out. + end + end + VALUE = VALUE .. unicode_codepoint_as_utf8(codepoint) + + elseif options.strictParsing then + --local next_byte = text:match('^\\(.)', i) printf("NEXT[%s]", next_byte); + self:onDecodeError("illegal use of backslash escape", text, i, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible + else + local byte_count = unicode_character_bytecount_based_on_first_byte(next_byte) + if byte_count == 0 then + self:onDecodeError("non-utf8 sequence after backslash escape", text, i, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible + end + + local next_character + if byte_count == 1 then + next_character = next_byte + elseif byte_count == 2 then + next_character = text:match('^(.[\128-\191])', i) + elseif byte_count == 3 then + next_character = text:match('^(.[\128-\191][\128-\191])', i) + elseif byte_count == 3 then + next_character = text:match('^(.[\128-\191][\128-\191][\128-\191])', i) + end + + if next_character == nil then + -- incomplete utf8 character after escape + self:onDecodeError("incomplete utf8 sequence after backslash escape", text, i, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible + end + + VALUE = VALUE .. next_character + i = i + byte_count + end + end + end + end + + self:onDecodeError("unclosed string", text, start, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible +end + +local function skip_whitespace(text, start) + + local _, match_end = text:find("^[ \n\r\t]+", start) -- [ https://datatracker.ietf.org/doc/html/rfc7158#section-2 ] + if match_end then + return match_end + 1 + else + return start + end +end + +local grok_one -- assigned later + +local function grok_object(self, text, start, options) + + if text:sub(start,start) ~= '{' then + self:onDecodeError("expected '{'", text, start, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible + end + + local i = skip_whitespace(text, start + 1) -- +1 to skip the '{' + + local VALUE = self.strictTypes and self:newObject { } or { } + + if text:sub(i,i) == '}' then + return VALUE, i + 1 + end + local text_len = text:len() + while i <= text_len do + local key, new_i = grok_string(self, text, i, options) + + i = skip_whitespace(text, new_i) + + if text:sub(i, i) ~= ':' then + self:onDecodeError("expected colon", text, i, options.etc) + return nil, i -- in case the error method doesn't abort, return something sensible + end + + i = skip_whitespace(text, i + 1) + + local new_val, new_i = grok_one(self, text, i, options) + + VALUE[key] = new_val + + -- + -- Expect now either '}' to end things, or a ',' to allow us to continue. + -- + i = skip_whitespace(text, new_i) + + local c = text:sub(i,i) + + if c == '}' then + return VALUE, i + 1 + end + + if text:sub(i, i) ~= ',' then + self:onDecodeError("expected comma or '}'", text, i, options.etc) + return nil, i -- in case the error method doesn't abort, return something sensible + end + + i = skip_whitespace(text, i + 1) + end + + self:onDecodeError("unclosed '{'", text, start, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible +end + +local function grok_array(self, text, start, options) + if text:sub(start,start) ~= '[' then + self:onDecodeError("expected '['", text, start, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible + end + + local i = skip_whitespace(text, start + 1) -- +1 to skip the '[' + local VALUE = self.strictTypes and self:newArray { } or { } + if text:sub(i,i) == ']' then + return VALUE, i + 1 + end + + local VALUE_INDEX = 1 + + local text_len = text:len() + while i <= text_len do + local val, new_i = grok_one(self, text, i, options) + + -- can't table.insert(VALUE, val) here because it's a no-op if val is nil + VALUE[VALUE_INDEX] = val + VALUE_INDEX = VALUE_INDEX + 1 + + i = skip_whitespace(text, new_i) + + -- + -- Expect now either ']' to end things, or a ',' to allow us to continue. + -- + local c = text:sub(i,i) + if c == ']' then + return VALUE, i + 1 + end + if text:sub(i, i) ~= ',' then + self:onDecodeError("expected comma or ']'", text, i, options.etc) + return nil, i -- in case the error method doesn't abort, return something sensible + end + i = skip_whitespace(text, i + 1) + end + self:onDecodeError("unclosed '['", text, start, options.etc) + return nil, i -- in case the error method doesn't abort, return something sensible +end + + +grok_one = function(self, text, start, options) + -- Skip any whitespace + start = skip_whitespace(text, start) + + if start > text:len() then + self:onDecodeError("unexpected end of string", text, nil, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible + end + + if text:find('^"', start) then + return grok_string(self, text, start, options) + + elseif text:find('^[-0123456789 ]', start) then + return grok_number(self, text, start, options) + + elseif text:find('^%{', start) then + return grok_object(self, text, start, options) + + elseif text:find('^%[', start) then + return grok_array(self, text, start, options) + + elseif text:find('^true', start) then + return true, start + 4 + + elseif text:find('^false', start) then + return false, start + 5 + + elseif text:find('^null', start) then + return options.null, start + 4 + + else + self:onDecodeError("can't parse JSON", text, start, options.etc) + return nil, 1 -- in case the error method doesn't abort, return something sensible + end +end + +function OBJDEF:decode(text, etc, options) + -- + -- If the user didn't pass in a table of decode options, make an empty one. + -- + if type(options) ~= 'table' then + options = {} + end + + -- + -- If they passed in an 'etc' argument, stuff it into the options. + -- (If not, any 'etc' field in the options they passed in remains to be used) + -- + if etc ~= nil then + options.etc = etc + end + + + -- + -- apply global options + -- + if options.decodeNumbersAsObjects == nil then + options.decodeNumbersAsObjects = self.decodeNumbersAsObjects + end + if options.decodeIntegerObjectificationLength == nil then + options.decodeIntegerObjectificationLength = self.decodeIntegerObjectificationLength + end + if options.decodeDecimalObjectificationLength == nil then + options.decodeDecimalObjectificationLength = self.decodeDecimalObjectificationLength + end + if options.decodeIntegerStringificationLength == nil then + options.decodeIntegerStringificationLength = self.decodeIntegerStringificationLength + end + if options.decodeDecimalStringificationLength == nil then + options.decodeDecimalStringificationLength = self.decodeDecimalStringificationLength + end + if options.strictParsing == nil then + options.strictParsing = self.strictParsing + end + + + if type(self) ~= 'table' or self.__index ~= OBJDEF then + local error_message = "JSON:decode must be called in method format" + OBJDEF:onDecodeError(error_message, nil, nil, options.etc) + return nil, error_message -- in case the error method doesn't abort, return something sensible + end + + if text == nil then + local error_message = "nil passed to JSON:decode()" + self:onDecodeOfNilError(error_message, nil, nil, options.etc) + return nil, error_message -- in case the error method doesn't abort, return something sensible + + elseif type(text) ~= 'string' then + local error_message = "expected string argument to JSON:decode()" + self:onDecodeError(string.format("%s, got %s", error_message, type(text)), nil, nil, options.etc) + return nil, error_message -- in case the error method doesn't abort, return something sensible + end + + -- If passed an empty string.... + if text:match('^%s*$') then + if options.strictParsing then + local error_message = "empty string passed to JSON:decode()" + self:onDecodeOfNilError(error_message, nil, nil, options.etc) + return nil, error_message -- in case the error method doesn't abort, return something sensible + else + -- we'll consider it nothing, but not an error + return nil + end + end + + if text:match('^%s*<') then + -- Can't be JSON... we'll assume it's HTML + local error_message = "HTML passed to JSON:decode()" + self:onDecodeOfHTMLError(error_message, text, nil, options.etc) + return nil, error_message -- in case the error method doesn't abort, return something sensible + end + + -- + -- Ensure that it's not UTF-32 or UTF-16. + -- Those are perfectly valid encodings for JSON (as per RFC 4627 section 3), + -- but this package can't handle them. + -- + if text:sub(1,1):byte() == 0 or (text:len() >= 2 and text:sub(2,2):byte() == 0) then + local error_message = "JSON package groks only UTF-8, sorry" + self:onDecodeError(error_message, text, nil, options.etc) + return nil, error_message -- in case the error method doesn't abort, return something sensible + end + + + -- + -- Finally, go parse it + -- + local success, value, next_i = pcall(grok_one, self, text, 1, options) + + if success then + + local error_message = nil + if next_i ~= #text + 1 then + -- something's left over after we parsed the first thing.... whitespace is allowed. + next_i = skip_whitespace(text, next_i) + + -- if we have something left over now, it's trailing garbage + if next_i ~= #text + 1 then + value, error_message = self:onTrailingGarbage(text, next_i, value, options.etc) + end + end + return value, error_message + + else + + -- If JSON:onDecodeError() didn't abort out of the pcall, we'll have received + -- the error message here as "value", so pass it along as an assert. + local error_message = value + if self.assert then + self.assert(false, error_message) + else + assert(false, error_message) + end + -- ...and if we're still here (because the assert didn't throw an error), + -- return a nil and throw the error message on as a second arg + return nil, error_message + + end +end + +local function backslash_replacement_function(c) + if c == "\n" then return "\\n" + elseif c == "\r" then return "\\r" + elseif c == "\t" then return "\\t" + elseif c == "\b" then return "\\b" + elseif c == "\f" then return "\\f" + elseif c == '"' then return '\\"' + elseif c == '\\' then return '\\\\' + elseif c == '/' then return '/' + else + return string.format("\\u%04x", c:byte()) + end +end + +local chars_to_be_escaped_in_JSON_string + = '[' + .. '"' -- class sub-pattern to match a double quote + .. '%\\' -- class sub-pattern to match a backslash + .. '/' -- class sub-pattern to match a forwardslash + .. '%z' -- class sub-pattern to match a null + .. '\001' .. '-' .. '\031' -- class sub-pattern to match control characters + .. ']' + + +local LINE_SEPARATOR_as_utf8 = unicode_codepoint_as_utf8(0x2028) +local PARAGRAPH_SEPARATOR_as_utf8 = unicode_codepoint_as_utf8(0x2029) +local function json_string_literal(value, options) + local newval = value:gsub(chars_to_be_escaped_in_JSON_string, backslash_replacement_function) + if options.stringsAreUtf8 then + -- + -- This feels really ugly to just look into a string for the sequence of bytes that we know to be a particular utf8 character, + -- but utf8 was designed purposefully to make this kind of thing possible. Still, feels dirty. + -- I'd rather decode the byte stream into a character stream, but it's not technically needed so + -- not technically worth it. + -- + newval = newval:gsub(LINE_SEPARATOR_as_utf8, '\\u2028'):gsub(PARAGRAPH_SEPARATOR_as_utf8,'\\u2029') + end + return '"' .. newval .. '"' +end + +local function object_or_array(self, T, etc) + -- + -- We need to inspect all the keys... if there are any strings, we'll convert to a JSON + -- object. If there are only numbers, it's a JSON array. + -- + -- If we'll be converting to a JSON object, we'll want to sort the keys so that the + -- end result is deterministic. + -- + local string_keys = { } + local number_keys = { } + local number_keys_must_be_strings = false + local maximum_number_key + + for key in pairs(T) do + if type(key) == 'string' then + table.insert(string_keys, key) + elseif type(key) == 'number' then + table.insert(number_keys, key) + if key <= 0 or key >= math.huge then + number_keys_must_be_strings = true + elseif not maximum_number_key or key > maximum_number_key then + maximum_number_key = key + end + elseif type(key) == 'boolean' then + table.insert(string_keys, tostring(key)) + else + self:onEncodeError("can't encode table with a key of type " .. type(key), etc) + end + end + + if #string_keys == 0 and not number_keys_must_be_strings then + -- + -- An empty table, or a numeric-only array + -- + if #number_keys > 0 then + return nil, maximum_number_key -- an array + elseif tostring(T) == "JSON array" then + return nil + elseif tostring(T) == "JSON object" then + return { } + else + -- have to guess, so we'll pick array, since empty arrays are likely more common than empty objects + return nil + end + end + + table.sort(string_keys) + + local map + if #number_keys > 0 then + -- + -- If we're here then we have either mixed string/number keys, or numbers inappropriate for a JSON array + -- It's not ideal, but we'll turn the numbers into strings so that we can at least create a JSON object. + -- + + if self.noKeyConversion then + self:onEncodeError("a table with both numeric and string keys could be an object or array; aborting", etc) + end + + -- + -- Have to make a shallow copy of the source table so we can remap the numeric keys to be strings + -- + map = { } + for key, val in pairs(T) do + map[key] = val + end + + table.sort(number_keys) + + -- + -- Throw numeric keys in there as strings + -- + for _, number_key in ipairs(number_keys) do + local string_key = tostring(number_key) + if map[string_key] == nil then + table.insert(string_keys , string_key) + map[string_key] = T[number_key] + else + self:onEncodeError("conflict converting table with mixed-type keys into a JSON object: key " .. number_key .. " exists both as a string and a number.", etc) + end + end + end + + return string_keys, nil, map +end + +-- +-- Encode +-- +-- 'options' is nil, or a table with possible keys: +-- +-- pretty -- If true, return a pretty-printed version. +-- +-- indent -- A string (usually of spaces) used to indent each nested level. +-- +-- align_keys -- If true, align all the keys when formatting a table. The result is uglier than one might at first imagine. +-- Results are undefined if 'align_keys' is true but 'pretty' is not. +-- +-- array_newline -- If true, array elements are formatted each to their own line. The default is to all fall inline. +-- Results are undefined if 'array_newline' is true but 'pretty' is not. +-- +-- null -- If this exists with a string value, table elements with this value are output as JSON null. +-- +-- stringsAreUtf8 -- If true, consider Lua strings not as a sequence of bytes, but as a sequence of UTF-8 characters. +-- (Currently, the only practical effect of setting this option is that Unicode LINE and PARAGRAPH +-- separators, if found in a string, are encoded with a JSON escape instead of as raw UTF-8. +-- The JSON is valid either way, but encoding this way, apparently, allows the resulting JSON +-- to also be valid Java.) +-- +-- +local function encode_value(self, value, parents, etc, options, indent, for_key) + + -- + -- keys in a JSON object can never be null, so we don't even consider options.null when converting a key value + -- + if value == nil or (not for_key and options and options.null and value == options.null) then + return 'null' + + elseif type(value) == 'string' then + return json_string_literal(value, options) + + elseif type(value) == 'number' then + if value ~= value then + -- + -- NaN (Not a Number). + -- JSON has no NaN, so we have to fudge the best we can. This should really be a package option. + -- + return "null" + elseif value >= math.huge then + -- + -- Positive infinity. JSON has no INF, so we have to fudge the best we can. This should + -- really be a package option. Note: at least with some implementations, positive infinity + -- is both ">= math.huge" and "<= -math.huge", which makes no sense but that's how it is. + -- Negative infinity is properly "<= -math.huge". So, we must be sure to check the ">=" + -- case first. + -- + return "1e+9999" + elseif value <= -math.huge then + -- + -- Negative infinity. + -- JSON has no INF, so we have to fudge the best we can. This should really be a package option. + -- + return "-1e+9999" + else + return tostring(value) + end + + elseif type(value) == 'boolean' then + return tostring(value) + + elseif type(value) ~= 'table' then + + if self.unsupportedTypeEncoder then + local user_value, user_error = self:unsupportedTypeEncoder(value, parents, etc, options, indent, for_key) + -- If the user's handler returns a string, use that. If it returns nil plus an error message, bail with that. + -- If only nil returned, fall through to the default error handler. + if type(user_value) == 'string' then + return user_value + elseif user_value ~= nil then + self:onEncodeError("unsupportedTypeEncoder method returned a " .. type(user_value), etc) + elseif user_error then + self:onEncodeError(tostring(user_error), etc) + end + end + + self:onEncodeError("can't convert " .. type(value) .. " to JSON", etc) + + elseif getmetatable(value) == isNumber then + return tostring(value) + else + -- + -- A table to be converted to either a JSON object or array. + -- + local T = value + + if type(options) ~= 'table' then + options = {} + end + if type(indent) ~= 'string' then + indent = "" + end + + if parents[T] then + self:onEncodeError("table " .. tostring(T) .. " is a child of itself", etc) + else + parents[T] = true + end + + local result_value + + local object_keys, maximum_number_key, map = object_or_array(self, T, etc) + if maximum_number_key then + -- + -- An array... + -- + local key_indent + if options.array_newline then + key_indent = indent .. tostring(options.indent or "") + else + key_indent = indent + end + + local ITEMS = { } + for i = 1, maximum_number_key do + table.insert(ITEMS, encode_value(self, T[i], parents, etc, options, key_indent)) + end + + if options.array_newline then + result_value = "[\n" .. key_indent .. table.concat(ITEMS, ",\n" .. key_indent) .. "\n" .. indent .. "]" + elseif options.pretty then + result_value = "[ " .. table.concat(ITEMS, ", ") .. " ]" + else + result_value = "[" .. table.concat(ITEMS, ",") .. "]" + end + + elseif object_keys then + -- + -- An object + -- + local TT = map or T + + if options.pretty then + + local KEYS = { } + local max_key_length = 0 + for _, key in ipairs(object_keys) do + local encoded = encode_value(self, tostring(key), parents, etc, options, indent, true) + if options.align_keys then + max_key_length = math.max(max_key_length, #encoded) + end + table.insert(KEYS, encoded) + end + local key_indent = indent .. tostring(options.indent or "") + local subtable_indent = key_indent .. string.rep(" ", max_key_length) .. (options.align_keys and " " or "") + local FORMAT = "%s%" .. string.format("%d", max_key_length) .. "s: %s" + + local COMBINED_PARTS = { } + for i, key in ipairs(object_keys) do + local encoded_val = encode_value(self, TT[key], parents, etc, options, subtable_indent) + table.insert(COMBINED_PARTS, string.format(FORMAT, key_indent, KEYS[i], encoded_val)) + end + result_value = "{\n" .. table.concat(COMBINED_PARTS, ",\n") .. "\n" .. indent .. "}" + + else + + local PARTS = { } + for _, key in ipairs(object_keys) do + local encoded_val = encode_value(self, TT[key], parents, etc, options, indent) + local encoded_key = encode_value(self, tostring(key), parents, etc, options, indent, true) + table.insert(PARTS, string.format("%s:%s", encoded_key, encoded_val)) + end + result_value = "{" .. table.concat(PARTS, ",") .. "}" + + end + else + -- + -- An empty array/object... we'll treat it as an array, though it should really be an option + -- + result_value = "[]" + end + + parents[T] = false + return result_value + end +end + +local function top_level_encode(self, value, etc, options) + local val = encode_value(self, value, {}, etc, options) + if val == nil then + --PRIVATE("may need to revert to the previous public verison if I can't figure out what the guy wanted") + return val + else + return val + end +end + +function OBJDEF:encode(value, etc, options) + if type(self) ~= 'table' or self.__index ~= OBJDEF then + OBJDEF:onEncodeError("JSON:encode must be called in method format", etc) + end + + -- + -- If the user didn't pass in a table of decode options, make an empty one. + -- + if type(options) ~= 'table' then + options = {} + end + + return top_level_encode(self, value, etc, options) +end + +function OBJDEF:encode_pretty(value, etc, options) + if type(self) ~= 'table' or self.__index ~= OBJDEF then + OBJDEF:onEncodeError("JSON:encode_pretty must be called in method format", etc) + end + + -- + -- If the user didn't pass in a table of decode options, use the default pretty ones + -- + if type(options) ~= 'table' then + options = default_pretty_options + end + + return top_level_encode(self, value, etc, options) +end + +function OBJDEF.__tostring() + return "JSON encode/decode package" +end + +OBJDEF.__index = OBJDEF + +function OBJDEF:new(args) + local new = { } + + if args then + for key, val in pairs(args) do + new[key] = val + end + end + + return setmetatable(new, OBJDEF) +end + +return OBJDEF:new() + +-- +-- Version history: +-- +-- 20211016.28 Had forgotten to document the strictParsing option. +-- +-- 20211015.27 Better handle some edge-case errors [ thank you http://seriot.ch/projects/parsing_json.html ; all tests are now successful ] +-- +-- Added some semblance of proper UTF8 parsing, and now aborts with an error on ilformatted UTF8. +-- +-- Added the strictParsing option: +-- Aborts with an error on unknown backslash-escape in strings +-- Aborts on naked control characters in strings +-- Aborts when decode is passed a whitespace-only string +-- +-- For completeness, when encoding a Lua string into a JSON string, escape a forward slash. +-- +-- String decoding should be a bit more efficient now. +-- +-- 20170927.26 Use option.null in decoding as well. Thanks to Max Sindwani for the bump, and sorry to Oliver Hitz +-- whose first mention of it four years ago was completely missed by me. +-- +-- 20170823.25 Added support for JSON:unsupportedTypeEncoder(). +-- Thanks to Chronos Phaenon Eosphoros (https://github.com/cpeosphoros) for the idea. +-- +-- 20170819.24 Added support for boolean keys in tables. +-- +-- 20170416.23 Added the "array_newline" formatting option suggested by yurenchen (http://www.yurenchen.com/) +-- +-- 20161128.22 Added: +-- JSON:isString() +-- JSON:isNumber() +-- JSON:decodeIntegerObjectificationLength +-- JSON:decodeDecimalObjectificationLength +-- +-- 20161109.21 Oops, had a small boo-boo in the previous update. +-- +-- 20161103.20 Used to silently ignore trailing garbage when decoding. Now fails via JSON:onTrailingGarbage() +-- http://seriot.ch/parsing_json.php +-- +-- Built-in error message about "expected comma or ']'" had mistakenly referred to '[' +-- +-- Updated the built-in error reporting to refer to bytes rather than characters. +-- +-- The decode() method no longer assumes that error handlers abort. +-- +-- Made the VERSION string a string instead of a number +-- + +-- 20160916.19 Fixed the isNumber.__index assignment (thanks to Jack Taylor) +-- +-- 20160730.18 Added JSON:forceString() and JSON:forceNumber() +-- +-- 20160728.17 Added concatenation to the metatable for JSON:asNumber() +-- +-- 20160709.16 Could crash if not passed an options table (thanks jarno heikkinen ). +-- +-- Made JSON:asNumber() a bit more resilient to being passed the results of itself. +-- +-- 20160526.15 Added the ability to easily encode null values in JSON, via the new "null" encoding option. +-- (Thanks to Adam B for bringing up the issue.) +-- +-- Added some support for very large numbers and precise floats via +-- JSON.decodeNumbersAsObjects +-- JSON.decodeIntegerStringificationLength +-- JSON.decodeDecimalStringificationLength +-- +-- Added the "stringsAreUtf8" encoding option. (Hat tip to http://lua-users.org/wiki/JsonModules ) +-- +-- 20141223.14 The encode_pretty() routine produced fine results for small datasets, but isn't really +-- appropriate for anything large, so with help from Alex Aulbach I've made the encode routines +-- more flexible, and changed the default encode_pretty() to be more generally useful. +-- +-- Added a third 'options' argument to the encode() and encode_pretty() routines, to control +-- how the encoding takes place. +-- +-- Updated docs to add assert() call to the loadfile() line, just as good practice so that +-- if there is a problem loading JSON.lua, the appropriate error message will percolate up. +-- +-- 20140920.13 Put back (in a way that doesn't cause warnings about unused variables) the author string, +-- so that the source of the package, and its version number, are visible in compiled copies. +-- +-- 20140911.12 Minor lua cleanup. +-- Fixed internal reference to 'JSON.noKeyConversion' to reference 'self' instead of 'JSON'. +-- (Thanks to SmugMug's David Parry for these.) +-- +-- 20140418.11 JSON nulls embedded within an array were being ignored, such that +-- ["1",null,null,null,null,null,"seven"], +-- would return +-- {1,"seven"} +-- It's now fixed to properly return +-- {1, nil, nil, nil, nil, nil, "seven"} +-- Thanks to "haddock" for catching the error. +-- +-- 20140116.10 The user's JSON.assert() wasn't always being used. Thanks to "blue" for the heads up. +-- +-- 20131118.9 Update for Lua 5.3... it seems that tostring(2/1) produces "2.0" instead of "2", +-- and this caused some problems. +-- +-- 20131031.8 Unified the code for encode() and encode_pretty(); they had been stupidly separate, +-- and had of course diverged (encode_pretty didn't get the fixes that encode got, so +-- sometimes produced incorrect results; thanks to Mattie for the heads up). +-- +-- Handle encoding tables with non-positive numeric keys (unlikely, but possible). +-- +-- If a table has both numeric and string keys, or its numeric keys are inappropriate +-- (such as being non-positive or infinite), the numeric keys are turned into +-- string keys appropriate for a JSON object. So, as before, +-- JSON:encode({ "one", "two", "three" }) +-- produces the array +-- ["one","two","three"] +-- but now something with mixed key types like +-- JSON:encode({ "one", "two", "three", SOMESTRING = "some string" })) +-- instead of throwing an error produces an object: +-- {"1":"one","2":"two","3":"three","SOMESTRING":"some string"} +-- +-- To maintain the prior throw-an-error semantics, set +-- JSON.noKeyConversion = true +-- +-- 20131004.7 Release under a Creative Commons CC-BY license, which I should have done from day one, sorry. +-- +-- 20130120.6 Comment update: added a link to the specific page on my blog where this code can +-- be found, so that folks who come across the code outside of my blog can find updates +-- more easily. +-- +-- 20111207.5 Added support for the 'etc' arguments, for better error reporting. +-- +-- 20110731.4 More feedback from David Kolf on how to make the tests for Nan/Infinity system independent. +-- +-- 20110730.3 Incorporated feedback from David Kolf at http://lua-users.org/wiki/JsonModules: +-- +-- * When encoding lua for JSON, Sparse numeric arrays are now handled by +-- spitting out full arrays, such that +-- JSON:encode({"one", "two", [10] = "ten"}) +-- returns +-- ["one","two",null,null,null,null,null,null,null,"ten"] +-- +-- In 20100810.2 and earlier, only up to the first non-null value would have been retained. +-- +-- * When encoding lua for JSON, numeric value NaN gets spit out as null, and infinity as "1+e9999". +-- Version 20100810.2 and earlier created invalid JSON in both cases. +-- +-- * Unicode surrogate pairs are now detected when decoding JSON. +-- +-- 20100810.2 added some checking to ensure that an invalid Unicode character couldn't leak in to the UTF-8 encoding +-- +-- 20100731.1 initial public release +-- \ No newline at end of file diff --git a/python/file/judge.lua b/python/file/judge.lua index 5dc317f..3e6280d 100644 --- a/python/file/judge.lua +++ b/python/file/judge.lua @@ -23,8 +23,13 @@ PLAN_ID=15 -- "方案ID不符",211 -- "检测项目不足,无法判定",212 -- "数据不合规",213 - - +-- 2023.5.11 +-- 修改脚本适配 雷管厂电解电容来料检测 +-- 2023.5.12 +-- 大于启停的时间从检测器异常改为芯片异常 +-- 2023.5.19 +-- 扫描uid,超出最大返回时间范围则显示芯片异常 +-- 添加起爆充能失败的判断,此时返回芯片异常 @@ -262,7 +267,7 @@ function JQ_Test_PowerPrapare(task,err_code) return nil else -- 上电正常,排除检测器异常 - del(err_code,1) + -- del(err_code,1) print("检测上电充能") -- 下一步是上电充能 return find_index(task["TaskIndex"]+2,1) @@ -300,8 +305,8 @@ function JQ_Test_PowerOn(task,err_code) print("检测电流") return find_index(task["TaskIndex"]+2,3) end - ---- 排除接触异常 - --del(err_code,3) + ---- 排除检测板异常 + del(err_code,1) print("检测芯片UID") -- 下一步检测芯片 return find_index(task["TaskIndex"]+2,4) @@ -319,6 +324,13 @@ function JQ_Test_ScanUID(task,err_code) only(err_code,5) return nil else + ret_value=find_return(task["TaskIndex"]+1) + -- 最大反馈时间在范围之外,芯片异常 + if(ret_value[2]task["TestStandard"][2]["Max"]) then + only(err_code,5) + return nil + end + print("检测电流") return find_index(task["TaskIndex"]+2,3) --print("读取芯片代码") @@ -416,12 +428,36 @@ function JQ_Test_BaseCurHigh(task,err_code) del(err_code,3) print("检测桥丝和电容") -- 下一步检测桥丝和电容 - return find_index(task["TaskIndex"]+2,12) + return find_index(task["TaskIndex"]+2,11) end end +-- 检测充能,失败则电容异常 +function JQ_Test_ChgEnergy(task,err_code) + if(find_ack(task["TaskIndex"]+1,mid(check_data,1,8))==false)then + only(err_code,2) + return nil + else + ret_value=find_return(task["TaskIndex"]+1) + if((ret_value[1]>task["TestStandard"][1]["Max"]) or (ret_value[1]task["TestStandard"][2]["Max"]) or (ret_value[2]task["TestStandard"][1]["Max"]) or (ret_value[1]39999) then + -- only(err_code,4) + -- return nil + -- else + -- only(err_code,2) + -- return nil + -- end + only(err_code,5) return nil else - -- 排除电容异常 - del(err_code,2) + -- 排除芯片异常,密码错误会导致起爆失败 del(err_code,5) print("检测完毕") return nil @@ -469,6 +520,8 @@ end + + -- 定义占位函数,不应该调用 function JQ_Test_Empty(task,err_code) print("err function call,task index=",task["TaskIndex"]) @@ -479,18 +532,29 @@ end -- 这个数组根据方案检测顺序确定 tasks_judge_fun={ - JQ_Test_PowerPrapare, - JQ_Test_PowerOn, - JQ_Test_ScanUID, - JQ_Test_BaseCurLow, - JQ_Test_ReadChipID, - JQ_Test_Empty, - JQ_Test_BaseCurHigh, - JQ_Test_Empty, - JQ_Test_CheckDAC, - JQ_Test_Empty, - JQ_Test_Empty, - JQ_Test_CheckDAC2 + JQ_Test_PowerPrapare,--0 + JQ_Test_PowerOn,--1 + JQ_Test_ScanUID,--2 + JQ_Test_BaseCurLow,--3 + JQ_Test_ReadChipID,--4 + JQ_Test_Empty,--5 + JQ_Test_Empty,--6 + JQ_Test_Empty,--7 + JQ_Test_BaseCurHigh,--8 + JQ_Test_Empty,--9 + JQ_Test_Empty,--10 + JQ_Test_Empty,--11 + JQ_Test_Empty,--12 + JQ_Test_Empty,--13 + JQ_Test_Empty,--14 + JQ_Test_Empty,--15 + JQ_Test_Empty,--16 + JQ_Test_ChgEnergy,--17 充能统计 + JQ_Test_Empty,--18 + JQ_Test_CheckDAC,--19 + JQ_Test_Empty,--20 + JQ_Test_Empty,--21 + JQ_Test_BoomEnergy,--22 } diff --git a/python/file/prints.lua b/python/file/prints.lua new file mode 100644 index 0000000..7f6966e --- /dev/null +++ b/python/file/prints.lua @@ -0,0 +1,84 @@ + + + +module = {} + +function module:print_t ( t ) + local print_r_cache={} + local function sub_print_r(t,indent) + if (print_r_cache[tostring(t)]) then + print(indent.."*"..tostring(t)) + else + print_r_cache[tostring(t)]=true + if (type(t)=="table") then + for pos,val in pairs(t) do + if (type(val)=="table") then + print(indent.."["..pos.."] => "..tostring(t).." {") + sub_print_r(val,indent..string.rep(" ",string.len(pos)+8)) + print(indent..string.rep(" ",string.len(pos)+6).."}") + elseif (type(val)=="string") then + print(indent.."["..pos..'] => "'..val..'"') + else + print(indent.."["..pos.."] => "..tostring(val)) + end + end + else + print(indent..tostring(t)) + end + end + end + if (type(t)=="table") then + print(tostring(t).." {") + sub_print_r(t," ") + print("}") + else + sub_print_r(t," ") + end + print() +end + + + + +function byte_to_hex(byte,size) + local str_table="0123456789ABCDEF" + local str_ret="" + if(size==2) then + str_ret=string.char( + string.byte( str_table,((byte>>12)&0xf)+1), + string.byte( str_table,((byte>>8)&0xf)+1), + string.byte( str_table,((byte>>4)&0xf)+1), + string.byte(str_table,(byte&0xf)+1)) + else + str_ret=string.char(string.byte(str_table,(byte%256)//16+1),string.byte(str_table,(byte%256)%16+1)) + end + return str_ret +end + +-- function byte_to_num(byte) +-- local str_ret="" +-- while true do + + +-- 打印数组 +function module:print_a ( a,size ,fmt) + local str="" + if(a==nil) then return end + if(size==nil) then size=1 end + for pos,val in pairs(a) do + str=str..byte_to_hex(val,size)..", " + end + print(str) +end + + + +function module:__tostring() + return "print table package" + end + + + + + +return module diff --git a/python/prottcp.py b/python/prottcp.py index 8310779..1d285b5 100644 --- a/python/prottcp.py +++ b/python/prottcp.py @@ -147,13 +147,23 @@ def arr_from_int(num:int): def scheme_get_task_range(j:json): t=bytearray() return_count=j["ReturnCount"] + t+=arr_from_int(return_count) + t+=arr_from_int(j["ExecuteErrCode"]) + index=0 for i in j["TestStandard"]: t+=arr_from_int(i["Max"]) t+=arr_from_int(i["Min"]) - n=return_count-len(j["TestStandard"]) + if (indexplan_id==0xffffffff){ cmd_print("scheme is empty."); return -1; } cmd_print("plan id: %d",s->plan_id); cmd_print("timeout_m: %d",s->timeout_m); - cmd_print("range num: %d",s->range_num); - for(int i=0;irange_num;i++) + cmd_print("task num: %d",s->task_num); + for(int i=0;itask_num;i++) { - cmd_print(" max:%5d, min:%5d %s",s->range[i].max,s->range[i].min, - s->range[i].maxrange[i].min?"err":"ok"); + t=&s->task[i]; + cmd_print(" task:%02d err:%02x",i,t->err); + for(int j=0;jitem_num;j++) + { + cmd_print(" max:%5d, min:%5d err:%02x %s",t->range[j].max,t->range[j].min, + t->err, + t->range[j].maxrange[j].min?"err":"ok"); + } } return 0; } @@ -228,6 +235,7 @@ static void print_sys_param(const sys_param_def *par) cmd_print("uart bsp: %d",par->uartbsp); cmd_print("coder ret mode: %d",par->coder_ret_mode); cmd_print("slave_addr_start: %d",par->slave_addr_start); + cmd_print("moter_max_count: %d",par->moter_max_count); } @@ -265,6 +273,7 @@ static void print_sys_help(void) cmd_print("set uartbsp 115200"); cmd_print("set codermode 1/0"); cmd_print("set slave_addr_start 1/0"); + cmd_print("set moter_max_count 0~30000"); cmd_print("set save"); } @@ -387,6 +396,12 @@ static int sys_set(list_def *argv) spar->slave_addr_start=str_atoi(str); cmd_print("slave addr start: %d",spar->slave_addr_start); } + else if(strcmp(list_get_str(argv,1),"moter_max_count")==0) + { + char *str=list_get_str(argv,2); + spar->moter_max_count=str_atoi(str); + cmd_print("moter_max_count: %d",spar->moter_max_count); + } else{ cmd_print("unknown cmd for sysset."); print_sys_help(); diff --git a/source/soft/mystdlib.c b/source/soft/mystdlib.c index a48d9d9..4e77273 100644 --- a/source/soft/mystdlib.c +++ b/source/soft/mystdlib.c @@ -188,7 +188,7 @@ void *realloc(void *p,size_t size) -#define MEM_TEMP_PTR_NUM 400 +#define MEM_TEMP_PTR_NUM 800 typedef void (*del_fun_def)(void *t); typedef struct{ diff --git a/source/task/key.c b/source/task/key.c index 380991f..fce1a1f 100644 --- a/source/task/key.c +++ b/source/task/key.c @@ -7,6 +7,7 @@ #include "prot_uc.h" #include "key.h" #include "mystdlib.h" +#include "transmit.h" @@ -16,6 +17,9 @@ typedef struct{ int inited; int run; + int input; + int input_lock; + void *tran; }self_def; @@ -26,36 +30,37 @@ static void request_check(void *p); static void key_thread(void *arg) { self_def *s=arg; - gpioin_def *in1,*in2,*in3,*in4; - int input=0,input_lock=1; + gpioin_def *in1; in1=dev_get("gpioin1"); - in2=dev_get("gpioin2"); - in3=dev_get("gpioin3"); - in4=dev_get("gpioin4"); in1->init(in1); - in2->init(in2); - in3->init(in3); - in4->init(in4); - + s->input=0; + s->input_lock=1; + while (s->run) { rt_thread_mdelay(20); - //input=(in1->state(in1))|(in2->state(in2)<<1)|(in3->state(in3)<<2)|(in4->state(in4)<<3); - input=(in1->state(in1)); - if(input!=input_lock) + s->input=(in1->state(in1)); + if(s->input!=s->input_lock) { - DBG_LOG("key=%08X",input); - input_lock=input; - if((input&1)==0) + DBG_LOG("key=%08X",s->input); + s->input_lock=s->input; + if((s->input&1)==0) { - request_check(s); + // 主机交互存在且非忙的时候才发送请求 + if(s->tran&&(tran_get_busy(s->tran)==0)) + request_check(s); } } } } - +// 获取实时按键状态,1按下 +int key_pressed(void) +{ + self_def *s=&g_self; + return !s->input; +} // cmd=0x37 @@ -81,7 +86,11 @@ static void init_later(void *t) DBG_WARN("can not fond variable \"protu\""); } } - +static void init_tran(void *t) +{ + self_def *s=t; + s->tran=app_variable("tran",0,0); +} @@ -95,6 +104,7 @@ static int init_thread(void) rt_thread_t rt_t=rt_thread_create("key_t",key_thread,s,1024,15,20); rt_thread_startup(rt_t); app_valid_call("protu",init_later,s); + app_valid_call("tran",init_tran,s); } return 0; } diff --git a/source/task/key.h b/source/task/key.h index d8b30ef..1b45097 100644 --- a/source/task/key.h +++ b/source/task/key.h @@ -16,6 +16,7 @@ signal key_send_signal(void *obj,uint8_t cmd,array_def *data); +int key_pressed(void); diff --git a/source/task/log.c b/source/task/log.c index 0d4a2af..d494ca1 100644 --- a/source/task/log.c +++ b/source/task/log.c @@ -64,10 +64,11 @@ static void log_thread(void *arg) else { DBG_LOG("udp:err=%d",err); + //netconn_close(conn); + //netconn_delete(conn); + rt_thread_mdelay(1000); } } - netconn_close(conn); - netconn_delete(conn); } diff --git a/source/task/moter.c b/source/task/moter.c index 7f98d1a..69b7098 100644 --- a/source/task/moter.c +++ b/source/task/moter.c @@ -96,7 +96,7 @@ void moter_down(void (*fun)(void *t),void *t) s->fun_in_end=fun; s->t=t; } - moter_start(0,MOTER_MAX_COUNT-s->count); + moter_start(0,s->max_count-s->count); } // ʱص @@ -177,8 +177,13 @@ int init_moter(void) pwm->set_irq_fun(pwm,end_irq,s); s->pwm=pwm; s->stat=INITING; - s->count=MOTER_MAX_COUNT; - s->max_count=MOTER_MAX_COUNT; + if((par->moter_max_count>25000)||(par->moter_max_count<1000)) + { + s->max_count=MOTER_MAX_COUNT; + }else{ + s->max_count=par->moter_max_count; + } + s->count=s->max_count; s->event=rt_event_create("moter_e",RT_IPC_FLAG_FIFO); s->inited=1; s->run=1; diff --git a/source/task/tran_for_checker.c b/source/task/tran_for_checker.c index 56313ae..89dc330 100644 --- a/source/task/tran_for_checker.c +++ b/source/task/tran_for_checker.c @@ -10,7 +10,7 @@ #include "compiler_info.h" #include "dev_backup.h" #include "moter.h" - +#include "key.h" @@ -25,24 +25,43 @@ typedef struct{ }check_def; - +// 计算方案有多少个范围参数 +static int calc_range_num(void) +{ + const scheme_def *s=check_scheme(); + const scheme_task_def *t; + int index=0; + for(int i=0;itask_num;i++) + { + t=&s->task[i]; + index+=t->item_num; + } + return index; +} // 判断检测数据是否超限,返回填充的字节个数 static int in_range(const uint8_t *src_data,uint8_t *range) { const scheme_def *s=check_scheme(); + const scheme_task_def *t; int temp; - for(int i=0;irange_num;i++) + int index=0; + for(int i=0;itask_num;i++) { - temp=src_data[i*2]|(src_data[i*2+1]<<8); - if(temp>=s->range[i].min&&temp<=s->range[i].max){ - range[i/8]&=~(1<<(i%8)); - }else{ - range[i/8]|=(1<<(i%8)); + t=&s->task[i]; + for(int j=0;jitem_num;j++) + { + temp=src_data[index*2]|(src_data[index*2+1]<<8); + if(temp>=t->range[j].min&&temp<=t->range[j].max){ + range[index/8]&=~(1<<(index%8)); + }else{ + range[index/8]|=(1<<(index%8)); + } + index++; } } - DBG_LOG("range_num=%d, ",s->range_num); - return (s->range_num+7)/8; + DBG_LOG("range_num=%d, ",index); + return (index+7)/8; } @@ -68,7 +87,7 @@ static void check_end(ucport_def *u,port_mcu *src,void *data,int ack,char *err_s // 返回参数校验,跳过8字节的流程执行位 ack_d+=in_range(arr_data(data)+8+8,ack_d); // 复制返回参数,跳过8字节的流程执行位 - memcpy(ack_d,arr_data(data)+8+8,check_scheme()->range_num*2); + memcpy(ack_d,arr_data(data)+8+8,calc_range_num()*2); } else { @@ -131,7 +150,7 @@ static void check_when_moter_down(void *t) static ucport_def *check(tran_def *t, uint8_t cmd,array_def *data) { - int return_size=(check_scheme()->range_num*2+8+(check_scheme()->range_num+7)/8); + int return_size=(calc_range_num()*2+8+(calc_range_num()+7)/8); check_def *u=calloc(1,sizeof(check_def)+return_size*20); u->ret_skip=return_size; u->ack_size=return_size*20; @@ -140,10 +159,15 @@ static ucport_def *check(tran_def *t, uint8_t cmd,array_def *data) u->u.dolater=check_dolater; u->u.doend=check_end; DBG_LOG("check: skip=%d",u->ret_skip); - tran_set_busy(t,1); - moter_down(check_when_moter_down,u); array_def *a=arr_creat(); - arr_append(a,0); + if(key_pressed()) + { + tran_set_busy(t,1); + moter_down(check_when_moter_down,u); + arr_append(a,TRAN_ERR_NONE); + }else{ + arr_append(a,TRAN_ERR_STEP); + } emit tran_reply_signal(u->u.p,arr_temp(a)); return (ucport_def *)u; diff --git a/source/task/tran_for_coder2.c b/source/task/tran_for_coder2.c index f2bdbb1..a14edd5 100644 --- a/source/task/tran_for_coder2.c +++ b/source/task/tran_for_coder2.c @@ -17,6 +17,13 @@ +typedef struct{ + int step;// 检测注码顺序 +}self_def; + +static self_def g_self; + + // 从机地址偏移 static inline int slave_addr_off(void) { @@ -85,6 +92,7 @@ static void write_uid_end(ucport_def *u,port_mcu *src,void *data,int ack,char *e } emit tran_send_signal(w->u.p,0x82,arr_temp(a)); tran_set_busy(w->u.p,0); + g_self.step=0; } } @@ -105,6 +113,7 @@ static void write_uid_return_ok(void *p) } emit tran_send_signal(u->u.p,0x82,arr_temp(a)); tran_set_busy(u->u.p,0); + g_self.step=0; } @@ -120,6 +129,7 @@ static ucport_def *write_uid(tran_def *t, uint8_t cmd,array_def *data) DBG_WARN("cmd format err."); return 0; } + int ret=0; write_uid_def *u=calloc(1,sizeof(write_uid_def)); u->u.p=t; u->u.del=write_del; @@ -135,10 +145,10 @@ static ucport_def *write_uid(tran_def *t, uint8_t cmd,array_def *data) memcpy(u->item[i].shell_code,d_off+i*14+1,13); DBG_LOG("slave:%d, shell=%s",i,u->item[i].shell_code); // 生成uid码 - coder_shell_to_uid(u->year,u->item[i].shell_code,u->item[i].uid_code); + ret|=coder_shell_to_uid(u->year,u->item[i].shell_code,u->item[i].uid_code); DBG_LOG("slave:%d, uid=%s",i,u->item[i].uid_code); // 生成存储码 - coder_uid_to_save(u->item[i].uid_code,u->item[i].uid_pw_hex); + ret|=coder_uid_to_save(u->item[i].uid_code,u->item[i].uid_pw_hex); // 生成密码 GetPasswordByUidCode((uint8_t *)u->item[i].uid_code,&u->item[i].uid_pw_hex[8]); d_=u->item[i].uid_pw_hex; @@ -155,6 +165,8 @@ static ucport_def *write_uid(tran_def *t, uint8_t cmd,array_def *data) u->ack[i*39+0]=i+slave_addr_off(); u->ack[i*39+1]=1; } + + tran_set_busy(t,1); for(int i=0;i<10;i++) { @@ -169,7 +181,14 @@ static ucport_def *write_uid(tran_def *t, uint8_t cmd,array_def *data) array_def *a=arr_creat(); arr_append(a,u->addrs_num); - arr_append(a,0); + if(g_self.step==1){ + if(ret==0) + arr_append(a,TRAN_ERR_NONE); + else + arr_append(a,TRAN_ERR_PARAM); + }else{ + arr_append(a,TRAN_ERR_STEP); + } emit tran_reply_signal(u->u.p,arr_temp(a)); // test:稍后返回成功 @@ -236,6 +255,45 @@ transmit_export(ym_checker,0x03,write_uid2) + + + +// 判断检测数据错误码 +// 返回 errcode,1=param/0=execute,taskindex,paramindex +static uint32_t in_range_err(const uint8_t *src_data,const uint8_t *errbit) +{ + const scheme_def *s=check_scheme(); + const scheme_task_def *t; + int temp; + int index=0; + uint32_t ret=0; + for(int i=0;itask_num;i++) + { + t=&s->task[i]; + if((errbit[i/8]&(1<<(i%8)))!=0) + { + ret=(t->err&0xff)|(i<<16); + return ret; + } + for(int j=0;jitem_num;j++) + { + temp=src_data[index*2]|(src_data[index*2+1]<<8); + if(temp>=t->range[j].min&&temp<=t->range[j].max){ + // 正常 + }else{ + ret=(t->range[j].err&0xff)|(1<<8)|(i<<16)|(j<<24); + return ret; + } + index++; + } + } + return ret; +} + + + + + typedef struct{ ucport_def u; uint8_t addrs_num; @@ -258,10 +316,16 @@ static void check_end(ucport_def *u,port_mcu *src,void *data,int ack,char *err_s } addr-=1; + uint32_t errcode=0; uint8_t *ack_d=&w->ack[(addr)*6]; ack_d[0]=addr+slave_addr_off(); ack_d[1]=ack; // TODO: 还有4字节数据 + errcode=in_range_err(arr_data(data)+8+8,arr_data(data)); + ack_d[2]=errcode&0xff; + ack_d[3]=(errcode>>8)&0xff; + ack_d[4]=(errcode>>16)&0xff; + ack_d[5]=(errcode>>24)&0xff; w->ack_num++; if(w->ack_num>=10) @@ -272,6 +336,7 @@ static void check_end(ucport_def *u,port_mcu *src,void *data,int ack,char *err_s arr_appends(a,w->ack,(6)*10); emit tran_send_signal(w->u.p,0x81,arr_temp(a)); tran_set_busy(w->u.p,0); + g_self.step=1; } } @@ -292,6 +357,7 @@ static void check_return_ok(void *p) } emit tran_send_signal(u->u.p,0x81,arr_temp(a)); tran_set_busy(u->u.p,0); + g_self.step=1; } @@ -335,7 +401,7 @@ static ucport_def *check(tran_def *t, uint8_t cmd,array_def *data) array_def *a=arr_creat(); arr_append(a,u->addrs_num); arr_append(a,u->type); - arr_append(a,0); + arr_append(a,TRAN_ERR_NONE); emit tran_reply_signal(u->u.p,arr_temp(a)); // test:稍后返回成功 diff --git a/source/task/transmit.c b/source/task/transmit.c index 466b8b7..419208c 100644 --- a/source/task/transmit.c +++ b/source/task/transmit.c @@ -116,6 +116,9 @@ void tran_recv_slot(tran_def *t,const char *codec_name,uint8_t cmd,array_def *da DBG_LOG("tran init"); if(tran_get_busy(tr)){ DBG_WARN("ucport is busy."); + array_def *d=arr_creat(); + arr_append(d,TRAN_ERR_BUSY); + emit tran_send_signal(tr,cmd,arr_temp(d)); return; } CHECK_DO(tr->u,tr->u->del); diff --git a/source/task/transmit.h b/source/task/transmit.h index 8ce02dc..10c3e1c 100644 --- a/source/task/transmit.h +++ b/source/task/transmit.h @@ -5,6 +5,18 @@ #include "handle.h" #include "signal.h" + + +// 错误码定义 +#define TRAN_ERR_NONE 0x00 +#define TRAN_ERR_BUSY 0x01 +#define TRAN_ERR_PARAM 0x02 +#define TRAN_ERR_STEP 0x03 + + + + + struct _tran_def; typedef struct _tran_def tran_def;