-
Notifications
You must be signed in to change notification settings - Fork 0
/
jm_hfcp.spin
470 lines (293 loc) · 13.9 KB
/
jm_hfcp.spin
1
'' ================================================================================================='''' File....... jm_hfcp.spin'' Purpose.... Human Friendly Control Protocol'' Author..... Jon "JonnyMac" McPhalen (aka Jon Williams)'' Copyright (c) 2011 Jon McPhalen'' -- see below for terms of use'' E-mail..... [email protected]'' Started.... '' Updated.... 18 NOV 2011'''' ================================================================================================={ Syntax header,server,command,P_MAX,{field1,field2, ... fieldn,}{*chksum,}CR Master to Node Header = ">" Node to Master Header = "<" Field-type identifiers "-", "0".."9" --> decimal "%" --> binary "$" --> hex "@" --> text All elements except head, checksum marker, and CR are variable-width ASCII}con REQ_HDR = ">" RSP_HDR = "<" FLD_SEP = "," EOM = 13 BIN_FLD = "%" HEX_FLD = "$" STR_MSG = "@" P_MAX = 8 ' number of fields suppported T_LEN = 40 Q_LEN = 128 ' input queue length R_LEN = 128 ' response buffer length #0, MSG_NONE, MSG_NUM, MSG_STR #0, P_DEC, P_BIN, P_HEX ' parameter type for output obj strings : "jm_strings" ' misc string methodsvar long msgtype ' true when message available long taddr ' target address long cmd ' command to target long pcount ' parameters count long param[P_MAX] ' parameter fields byte tfield[T_LEN+1] ' text field byte queue[Q_LEN] ' character input queue byte qidx byte rbuf[Q_LEN] ' response buffer byte ridx byte ptype[P_MAX] ' parameter type (for output) byte pwidth[P_MAX] ' parameter field width (for output)con ' remove after testingpub start'' Initialize all reset ' clear message vars bytefill(@ptype, P_DEC, P_MAX) ' default to decimal output bytefill(@pwidth, 0, P_MAX) bytefill(@queue, 0, Q_LEN+1) ' clear queue + index pub reset'' Reset all HFCP variables msgtype := MSG_NONE ' reset to no message taddr := -1 cmd := -1 pcount := 0 longfill(@param, 0, P_MAX) ' clear parameters bytefill(@tfield, 0, T_LEN + 1) ' clear text field bytefill(@rbuf, 0, R_LEN+1) ' clear response buffer pub enqueue(c) | fc'' Accepts new character into queue if ((qidx == 0) and (c <> REQ_HDR)) ' wait for header return else if ((c <> EOM) and (c <> 0)) ' not end of message? queue[qidx++] := c ' move c to queue else reset ' clear last fc := field_count ' count fields in queue if (fc => 2) msgtype := parse_fields ' if good, parse it bytefill(@queue, 0, Q_LEN+1) ' reset queuepub has_msg'' Returns message type (0 [false] for no message) return msgtypepub clear_msg'' Clears current message flag msgtype := MSG_NONE pub target'' Return target address from last message return taddr pub command'' Return command from last message return cmd pub p_count'' Return parameters count from last message return pcount pub p_read(pnum) | value'' Reads numeric parameter from last message value := 0 if (msgtype == MSG_NUM) ' is message available if ((pnum => 1) and (pnum =< P_MAX)) ' valid field value := param[pnum-1] ' return parameter return valuepub p_addr'' Returns address of parameters return @parampub str'' Returns pointer to text data return @tfield pub write(pnum, value)'' Writes value to numeric parameter if ((pnum => 1) and (pnum =< P_MAX)) param[pnum-1] := valuepub writex(pnum, value, type, width)'' Writes value to numeric parameter and sets type and width if ((pnum => 1) and (pnum =< P_MAX)) param[pnum-1] := value case type P_BIN: ptype[pnum-1] := P_BIN pwidth[pnum-1] := 1 #> width <# 32 P_HEX: ptype[pnum-1] := P_HEX pwidth[pnum-1] := 1 #> width <# 8 other: ptype[pnum-1] := P_DEC pwidth[pnum-1] := 0 #> width <# 11 pub write_block(bcount, bpntr)'' Writes block of longs to parameters if (bcount > 0) ' if valid bcount <#= P_MAX ' limit to P_MAX longmove(@param, bpntr, bcount) ' copy block at bptr to param[]pub write_str(pntr) | slen bytefill(@tfield, 0, T_LEN) ' clear slen := strsize(pntr) ' get length of output if (slen =< T_LEN) ' if it fits bytemove(@tfield, pntr, slen) ' copy whole string else bytemove(@tfield, pntr, T_LEN) ' leave last 0 pcount := STR_MSG ' set message typepub build_msg(tgtresp, cmdresp, pcnt) | ts, pidx bytefill(@rbuf, 0, R_LEN+1) ' clear response buffer rbuf[ridx++] := RSP_HDR ' response header dec(tgtresp) ' target responding rbuf[ridx++] := FLD_SEP dec(cmdresp) ' command response if (pcnt == STR_MSG ) ' string response? rbuf[ridx++] := FLD_SEP rbuf[ridx++] := STR_MSG rbuf[ridx++] := FLD_SEP ts := strsize(@tfield) ' get size of text field bytemove(@rbuf[ridx], @tfield, ts) ' move into queue ridx += ts ' update index elseif (pcnt > 0) ' return parameters? pcnt <#= P_MAX ' limit rbuf[ridx++] := FLD_SEP dec(pcnt) ' parameter count repeat pidx from 0 to (pcnt-1) ' loop for each rbuf[ridx++] := FLD_SEP ' separate field case ptype[pidx] ' output by type P_BIN: bin(param[pidx], pwidth[pidx]) P_HEX: hex(param[pidx], pwidth[pidx]) other: if (pwidth[pidx] == 0) ' variable width? dec(param[pidx]) else rjdec(param[pidx], pwidth[pidx]) rbuf[ridx] := EOM ' set eom marker return @rbuf ' return pointer to bufferpub response'' Returns pointer to response buffer'' -- for use in string routines return @rbuf conpub field_count | fc, idx '' Returns number of fields in HFCP message fc := idx := 0 ' initialize count, index repeat case queue[idx++] FLD_SEP: ' field separator? ++fc ' inc field count EOM, 0: ' end of message ++fc ' inc field count quit ' done return fcpri parse_fields | pos, fc, idx'' Parse field data from string in queue'' -- returns type of message in queue reset ' clear everything except queue taddr := strings.asc2val(@queue[1]) ' extract target address pos := field_pos(1) cmd := strings.asc2val(@queue+pos) ' extract command pos := field_pos(2) if (strings.instr(@queue, string(STR_MSG)) => 0) ' marked as string? pos := field_pos(3) bytemove(@tfield, @queue+pos, strsize(@queue+pos)) return MSG_STR pcount := strings.asc2val(@queue+pos) ' extract parameters count if (pcount > 0) ' have parameters? pcount <#= P_MAX ' keep legal repeat idx from 0 to pcount-1 ' loop through parameter fields pos := field_pos(3+idx) ' find field position param[idx] := strings.asc2val(@queue+pos) ' extract parameter return MSG_NUM pri field_pos(fn) | idx '' Returns position index of field number, fn (0+) if (fn < 0) ' must be positive return -1 idx := 0 ' start at beginning repeat while (fn > 0) ' loop through field count case queue[idx++] ' get character from queue FLD_SEP: ' if separator --fn ' decrement field EOM, 0: ' if end-of-message idx := -1 ' bad field quit return idxcon ' output to type ' -- most borrowed/adapted from FullDuplexSerial by Chip Graceypri rjdec(val, width) | tmpval, pad' Original code by Dave Hein' Modifications by Jon McPhalen if (width =< 0) return width <#= 11 if (val => 0) tmpval := val pad := width - 1 else if (val == negx) tmpval := posx else tmpval := -val pad := width - 2 repeat while (tmpval => 10) --pad tmpval /= 10 repeat pad rbuf[ridx++] := "0" dec(val) pri dec(value) | i, x x := value == negx if value < 0 value := ||(value+x) rbuf[ridx++] := "-" i := 1_000_000_000 repeat 10 if value => i rbuf[ridx++] := value / i + "0" + x*(i == 1) value //= i result~~ elseif result or (i == 1) rbuf[ridx++] := "0" i /= 10 pri bin(value, digits) if (digits =< 0) return rbuf[ridx++] := "%" digits <#= 32 value <<= 32 - digits repeat digits rbuf[ridx++] := (value <-= 1) & 1 + "0"pri hex(value, digits) if (digits =< 0) return rbuf[ridx++] := "$" digits <#= 8 value <<= (8 - digits) << 2 repeat digits rbuf[ridx++] := lookupz((value <-= 4) & $F : "0".."9", "A".."F") dat{{ Terms of Use: MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.}}