復制代碼- /*-
 -  * Copyright (c) 2017 moecmks
 -  * All rights reserved.
 -  *
 -  * Redistribution and use in source and binary forms, with or without
 -  * modification, are permitted provided that the following conditions
 -  * are met:
 -  * 1. Redistributions of source code must retain the above copyright
 -  *    notice, this list of conditions and the following disclaimer.
 -  * 2. Redistributions in binary form must reproduce the above copyright
 -  *    notice, this list of conditions and the following disclaimer in the
 -  *    documentation and/or other materials provided with the distribution.
 -  *
 -  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 -  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 -  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 -  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 -  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 -  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 -  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 -  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 -  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 -  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 -  * SUCH DAMAGE.
 -  *
 -  */
 -  
 - #include <assert.h>
 - #include <string.h>
 - #include "codecb.h"
 - /* Convert characters to specific number - noexport */
 - static 
 - char ascii_to_num (char ch) {
 -   /* e.g.
 -    *  source '9' -> target 9
 -    *  source 'A' -> target 10
 -    *  source '1' -> target 1
 -    *  source 'a' -> (nondone, Don't use lowercase letters in fx1s-14mr-001).
 -    */
 -   if (ch >= '0' && ch <= '9')
 -     return (ch - '0');
 -   if (ch >= 'A' && ch <= 'F')
 -     return (ch - ('A' - 10));
 -   else
 -     assert (0);
 -   return ch;
 - }
 - /* Convert number to specific characters - noexport */
 - static 
 - char num_to_ascii (char ch) {
 -   /* e.g.
 -    *  source 9 -> target '9'
 -    *  source A -> target '0'
 -    *  source 1 -> target '1'
 -    *  source a -> (nondone, Don't use lowercase letters in fx1s-14mr-001).
 -    */
 -   if (ch >= 0x00 && ch <= 0x09)
 -     return (ch + '0');
 -   if (ch >= 0x0A && ch <= 0x0F)
 -     return (ch + ('A' - 10));
 -   else
 -     assert (0);
 -   return ch;
 - }
 - /* XXX:L-endian.
 - */
 - static 
 - uint16_t vailed8 (uint16_t nums) {
 -   
 -   /* e.g. 
 -    * 1234 vailed.
 -    * 9000 invailed.
 -    * 1007 vailed.
 -    * 1811 invailed.
 -    * 0 ~ 65535
 -    */
 -    uint16_t d0 = nums % 10 >> 0;
 -    uint16_t d1 = nums % 100 / 10;
 -    uint16_t d2 = nums % 1000 / 100;
 -    uint16_t d3 = nums % 10000 / 1000;
 -    
 -    if ( d0 > 7 || d1 > 7)
 -      return -1;
 -    if ( d2 > 7)
 -      return -1;
 -    return d0 + d1 * 8 + d2 * 8 * 8 + d3 * 8 * 8 * 8;
 - }
 - static 
 - uint8_t fxcrc_adjust (unsigned char *crcbuf, uint32_t num) {
 -   
 -   uint32_t s= 0;
 -   uint32_t st = 0;
 -   
 -   for (; s != num; s++)
 -     st += crcbuf[s];
 -   /* we only save lowest bit's byte **/
 -   return st & 0xFF; 
 - }
 - static /* we not check numb cross register **.**/
 - uint32_t fxcalc_addru (enum FXREGS_FIELD reg, uint16_t addr, 
 -                            enum FX1S_VERSION ver, 
 -                            uint16_t  *opbsize,
 -                                 uint16_t *raddr, unsigned char *dboff) {
 -   
 -   static const /* XXX:enum constant dependence **/
 -                    uint16_t xmax_tab[4] = { 6, 8, 12, 16 };
 -   static const /* XXX:enum constant dependence **/
 -                    uint16_t ymax_tab[4] = { 4, 6, 8, 14 };
 -   uint16_t addr0 = 0x00A0;
 -   uint16_t eig = vailed8 (addr);
 -   uint8_t off = -1;
 -   uint32_t opbsize0 = 2;
 -   switch (reg) {
 -     
 -   case FX1S_REGISTER_FIELD_D: 
 -   
 -     if (addr <= 127 && addr == addr) /* numbers: 128, normal use */
 -       addr0 = 0x1000 + addr * 2;
 -     else if (addr <= 255) /* numbers: 128, save use */
 -       addr0 = 0x1000 + addr * 2;
 -     else if (addr >= 1000 && addr <= 2499)  /* numbers: 1500, file register */
 -       addr0 = 0x1000 + addr * 2;
 -     else if (addr >= 8000 && addr <= 8255) /* numbers: 256, special IO port */
 -       addr0 = 0x0E00 + (addr - 8000) * 2;
 -     else  /* Illegal access */
 -       return FX1S_RANGE;
 -     break;
 -   case FX1S_REGISTER_FIELD_X:
 -     /*
 -      * Check the number of available X-coils according to the PLC version 
 -      */
 -     if ((eig = vailed8 (addr)) == -1
 -      || (eig >= xmax_tab[ver]) )
 -       return FX1S_PARA;
 -       
 -     addr0 = 0x0080 + eig / 8;
 -     off = eig & 7;
 -     
 -     opbsize0 = 1;
 -     break;
 -     
 -   case FX1S_REGISTER_FIELD_Y_PLS:
 -     addr0 += 0x0200;
 -   case FX1S_REGISTER_FIELD_Y_OUT:
 -   
 -     /*
 -      * Check the number of available Y-coils according to the PLC version 
 -      */
 -     if ((eig = vailed8 (addr)) == -1)
 -       return FX1S_PARA;
 -     
 -     addr0 += eig / 8;
 -     off = eig & 7;
 -     
 -     opbsize0 = 1;
 -     break;
 -   case FX1S_REGISTER_FIELD_S:
 -   
 -     if ((addr >=  128)) /* numbers:128, status register **/
 -       return FX1S_RANGE;
 -     addr0 = addr / 8;
 -     off = addr & 7;
 -     
 -     opbsize0 = 1;
 -     break;
 -   
 -   case FX1S_REGISTER_FIELD_T:
 -   
 -     if ( (addr <=  63)) /* numbers:64, 100ms or 10ms M8028/D8030/D8031 **/
 -       addr0 = 0x0800 + addr * 2;
 -     else     
 -       return FX1S_RANGE;
 -     break;
 -     
 -   case FX1S_REGISTER_FIELD_M:
 -   
 -     if (addr < 384) /* numbers: 384, normal use */
 -       addr0 = 0x0100 + addr / 8;
 -     else if (addr < 512) /* numbers: 512, save use */
 -       addr0 = 0x0100 + addr / 8;
 -     else if (addr >= 8000 && addr < 8256) /* numbers: 256, special IO port */
 -       addr0 = 0x01E0 + (addr - 8000) / 8;
 -     else /* Illegal access */
 -       return FX1S_RANGE;
 -     
 -     off = addr & 7;
 -     opbsize0 = 1;
 -     break;
 -   
 -   case FX1S_REGISTER_FIELD_C16:
 -   
 -     if (addr < 16) /* numbers: 16, normal use */
 -       addr0 = 0x0A00 + addr * 2;
 -     else if (addr < 32) /* numbers: 16, save use */
 -       addr0 = 0x0A00 + addr * 2;
 -     else /* Illegal access */
 -       return FX1S_RANGE;
 -     break;
 -     
 -   case FX1S_REGISTER_FIELD_C32:
 -   
 -    /* for C32 high speed registers, 
 -     * we only perform some basic checks, please note
 -     **/
 -     if (addr > 200 && addr <= 255)
 -       addr0 = 0x0C00 + (addr - 200) * 4;
 -     else /* Illegal access */
 -       return FX1S_RANGE;
 -       
 -     opbsize0 = 4;
 -     break;
 -   
 -   case FX1S_REGISTER_FIELD_CRESET:
 -   
 -     if (addr <= 255)
 -       addr0 = 0x03C0 + addr / 8;
 -     else /* Illegal access */
 -       return FX1S_RANGE;
 -       
 -     opbsize0 = 1;
 -     break;
 -     
 -   default:
 -       return FX1S_PARA;
 -   }
 -   
 -   *raddr = addr0;
 -   *dboff = off;
 -   *opbsize = opbsize0;
 -   return FX1S_OK;
 - }
 - int fx1s_makersecb (struct read_section2 *rsec, /* write to the serial port, use the size of the read_section */
 -                          enum FX1S_REGISTER_FIELD rf, uint16_t  *rvap_size,
 -                          enum FX1S_VERSION ver, uint16_t address)
 - {
 -   struct read_section2 sec;
 -   uint32_t e;
 -   
 -   /** phase 1:fill stdhead/stdend flags and cmd, rread count,s */
 -   sec.stx = SECTION_LINK_STX;
 -   sec.etx = SECTION_LINK_ETX;
 -   sec.cmd = SECTION_CMD_READ;
 -   /** phase 2:calc address for register and current PLC version */
 -   e = fxcalc_addru (rf, address, ver, & sec.opbsize, & sec.opbaddr, & sec.opboff);
 -   if (e != FX1S_OK) 
 -     return e;
 -   else 
 -    *rvap_size = sizeof (sec.stx) +
 -                 sizeof (sec.crc)+ sizeof (sec.etx) + sec.opbsize * 2;
 -   /** phase 3:fill numb ascii, * */
 -   sec.numb[0] = num_to_ascii ( (sec.opbsize  & 0xF0) >>4);
 -   sec.numb[1] = num_to_ascii ( (sec.opbsize  & 0x0F) >>0);
 -   
 -   /** phase 4:fill address ascii, * */
 -   sec.unit_address[0] = num_to_ascii ( (sec.opbaddr  & 0xF000) >>12);
 -   sec.unit_address[1] = num_to_ascii ( (sec.opbaddr  & 0x0F00) >> 8);
 -   sec.unit_address[2] = num_to_ascii ( (sec.opbaddr  & 0x00F0) >> 4);
 -   sec.unit_address[3] = num_to_ascii ( (sec.opbaddr  & 0x000F) >> 0);
 -   
 -   /** phase 5:crc adjust, fill ascii buf * */
 -   sec.crce = fxcrc_adjust (& sec.cmd, sizeof (sec.cmd) + sizeof (sec.unit_address) 
 -                                         + sizeof (sec.numb) 
 -                                         + sizeof (sec.etx));
 -   sec.crc[0] = num_to_ascii ( (sec.crce  & 0xF0) >> 4);
 -   sec.crc[1] = num_to_ascii ( (sec.crce  & 0x0F) >> 0);
 -   
 -   memcpy (rsec, & sec, sizeof (sec));
 -   return FX1S_OK;  
 - }
 - int fx1s_makewsecb (void *wsec, /* Variable size structure, so use void *, please understand **/
 -                    void *buf, /* wsec size == sizeof(wc) * 2  **/
 -                          enum FX1S_REGISTER_FIELD rf, uint16_t *wsec_size,
 -                          enum FX1S_VERSION ver, uint16_t address) 
 - {
 -   uint16_t opbsize, opbaddr;
 -   char obpoff;
 -   char varsbuf[256];
 -   char *as = buf, cs;
 -   uint32_t e;
 -   uint32_t s = 0;
 -   struct write_section *secp = wsec;
 -   struct write_section *secdp = (void *)varsbuf;
 -   /** phase 1:fill stdhead flags and cmd */
 -   secdp->stx = SECTION_LINK_STX;
 -   secdp->cmd = SECTION_CMD_WRITE;
 -   /** phase 2:calc address for register and current PLC version */
 -   e = fxcalc_addru (rf, address, ver, & opbsize, & opbaddr, & obpoff);
 -   if (e != FX1S_OK) 
 -     return e;
 -   else 
 -     *wsec_size = sizeof (struct write_section) + opbsize * 2;
 -   /** phase 3:fill numb ascii, * */
 -   secdp->numb[0] = num_to_ascii ( (opbsize  & 0xF0) >>4);
 -   secdp->numb[1] = num_to_ascii ( (opbsize  & 0x0F) >>0);
 -   
 -   /** phase 4:fill address ascii, * */
 -   secdp->unit_address[0] = num_to_ascii ( (opbaddr  & 0xF000) >>12);
 -   secdp->unit_address[1] = num_to_ascii ( (opbaddr  & 0x0F00) >> 8);
 -   secdp->unit_address[2] = num_to_ascii ( (opbaddr  & 0x00F0) >> 4);
 -   secdp->unit_address[3] = num_to_ascii ( (opbaddr  & 0x000F) >> 0);
 -   
 -   /** phase 5:fill variable buffer, * */
 -   for ( ; s != opbsize; s++) {
 -     unsigned char  temp = as[s];
 -     char  tmphi = num_to_ascii (temp >> 4);
 -     char  tmplo = num_to_ascii (temp & 15);
 -     
 -     secdp->numb[2+s*2+0] = tmphi;
 -     secdp->numb[2+s*2+1] = tmplo;
 -   }
 -   /** phase 6:crc adjust, fill ascii buf * */
 -   secdp->numb[2+opbsize*2] = SECTION_LINK_ETX;
 -   
 -   cs = fxcrc_adjust (& secdp->cmd, opbsize * 2 + sizeof (secp->cmd) + sizeof (secp->unit_address) 
 -                                         + sizeof (secp->numb) 
 -                                         + sizeof (secp->etx));
 -   secdp->numb[2+opbsize*2+1] = num_to_ascii ( (cs  & 0xF0) >> 4);
 -   secdp->numb[2+opbsize*2+2] = num_to_ascii ( (cs  & 0x0F) >> 0);
 -   
 -   memcpy (wsec, & varsbuf, *wsec_size);
 -   return FX1S_OK; 
 - }
 - uint32_t fx1s_cmprvpack (void *raccbuf, /* Variable size structure, so use void *, please understand **/
 -                          uint16_t rc, void **ascii_buf, uint16_t *opbsize
 -                          , uint16_t *stdpos)
 - {
 -   char *varsbuf = raccbuf;
 -   uint16_t c = 0;
 -   char stx_find = 0;
 -   uint16_t stdpos0 = -1;
 -   
 -   /* we find SECTION_LINK_NAK or SECTION_LINK_STX at first **/
 -   for (; c != rc; c++)
 -    {
 -      if (varsbuf[c] == SECTION_LINK_NAK)
 -        return FX1S_NAK;
 -      if (varsbuf[c] == SECTION_LINK_STX)
 -       {
 -         /* second, we check SECTION_LINK_ETX in buffer **/
 -         stx_find = 1;
 -         stdpos0 = c + 1;
 -       }  
 -      if (varsbuf[c] == SECTION_LINK_ETX && stx_find == 1)
 -       {
 -         /* exist CRC byte ??**/
 -         if ((c + 2) >= rc)
 -           return FX1S_INCOP;
 -         /* calculate, compare the CRC code **/
 -         {
 -       # if 0
 -       # else 
 -           *ascii_buf = & varsbuf[stdpos0];
 -           *opbsize = c - stdpos0;
 -           *stdpos = stdpos0;
 -           return FX1S_OK;
 -       # endif    
 -         }
 -       }
 -    }
 -    
 -    return FX1S_INCOP;
 - }                     
 - uint32_t fx1s_decrvsec (void *raccbuf, void *sbuf, uint16_t opbasize) {
 -   
 -   char *varsbuf = raccbuf;
 -   char *ssbuf = sbuf;
 -   uint16_t c = 0;
 -   
 -   if (opbasize % 2 == 1)
 -     return FX1S_INCOP;
 -   if (opbasize == 0)
 -     return FX1S_PARA;
 -   
 -   for ( ; c != opbasize; c += 2)
 -     {
 -       char tmphi = ascii_to_num (varsbuf[c]) << 4;
 -       char tmplo = ascii_to_num (varsbuf[c+1]);   
 -       char temp  =   (tmphi & 0xF0) |    (tmplo & 0x0F);
 -       
 -       ssbuf[c>>1] = temp;
 -     }
 -     
 -     return FX1S_OK;
 - }
 
  |