/** * Запустить приложение в пакетном режиме * * @param код результата, который вернуло приложение * @param режим открытия информационной базы. Например: конфигуратор или предприятие * @param аргументы запуска (может быть пустым) * * @result код результата и информация об ошибке (если есть) */ func (ib InformationBase) runApplication(mode string, args []string) (resultCode int, err error) { fullargs := []string{mode} ib.addAccessString(mode, &fullargs) for _, v := range args { if len(v) > 0 { fullargs = append(fullargs, v) } } addPathToLog(mode, &fullargs) cmd := exec.Command(ib.pathTo1C, fullargs...) utils.AddTextToLog(utils.LogLevel_INFO, "Командная строка: "+strings.Join(cmd.Args, " ")) if err = cmd.Run(); err == nil { waitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus) resultCode = waitStatus.ExitStatus() } else { utils.AddTextToLog(utils.LogLevel_ERROR, err.Error()) if exitError, ok := err.(*exec.ExitError); ok { waitStatus := exitError.Sys().(syscall.WaitStatus) resultCode = waitStatus.ExitStatus() } } utils.AddTextToLog(utils.LogLevel_INFO, fmt.Sprintf("Код результата: %d", resultCode)) return }
// Инициализация объекта данными из файла func (c *ConfCf) InitFromFile(fileData []byte) error { utils.AddTextToLog(utils.LogLevel_INFO, "Начало инициализации объекта данными конфигурационного файла") if len(fileData) <= len(groupBlocksFlag) { return errors.New("Не верный размер файла конфигурационного файла") } if !bytes.Equal(fileData[:len(groupBlocksFlag)], groupBlocksFlag) { return errors.New("Данные не соотвествуют файлу конфигурационному файлу") } bloksHeadersPairs, err := readTOC(fileData) if err != nil { return err } else if len(bloksHeadersPairs) == 0 { return errors.New("Ошибка чтения заголовка.") } funcForInitBlocks := func(numStream int, processBlocksTotal int, countBlocks int, blockInitResultChanal chan bool) { for i := processBlocksTotal; i < countBlocks && i-processBlocksTotal != numStream; i++ { pair := bloksHeadersPairs[i] go c.blocksList[i].Init(fileData, pair.attrs, pair.data, blockInitResultChanal) } } c.initBlocks(len(bloksHeadersPairs), funcForInitBlocks) utils.AddTextToLog(utils.LogLevel_INFO, "-Окончание инициализации объекта данными конфигурационного файла") return nil }
// Инициализировать блок из данных сохраненных блоков в файлы func (b *block) InitFromFiles(pathToDir string, fileInfo os.FileInfo, c chan<- bool) { utils.AddTextToLog(utils.LogLevel_TRACE, "Начало инициализации блока") var err error b.attrs.name = fileInfo.Name() currentTime := time.Now().UnixNano() / int64(time.Millisecond) fullPathToFile := path.Join(pathToDir, b.attrs.name) funcGenerateErr := func(errorNum int) { err = errors.New(fmt.Sprintf("Ошибка инициализации блока из даных файла №%d: %s", errorNum, err.Error())) utils.AddTextToLog(utils.LogLevel_ERROR, err.Error()) panic(err.Error()) } if fileInfo.IsDir() { b.blockType = blockType_multiple var files []os.FileInfo if files, err = utils.ReadFilesInDir(fullPathToFile); err != nil { funcGenerateErr(1) } b.subBlocks = make([]subBlock, len(files)) for i, fi := range files { b.subBlocks[i].Attrs.name = fi.Name() b.subBlocks[i].Attrs.creationDate = currentTime b.subBlocks[i].Attrs.modificationDate = b.subBlocks[i].Attrs.creationDate pathToSubBlock := path.Join(fullPathToFile, fi.Name()) if b.subBlocks[i].Data, err = ioutil.ReadFile(pathToSubBlock); err != nil { funcGenerateErr(2) } } } else { b.blockType = blockType_simply if b.data, err = ioutil.ReadFile(fullPathToFile); err != nil { funcGenerateErr(3) } } b.attrs.creationDate = currentTime b.attrs.modificationDate = b.attrs.creationDate if b.IsForm() { b.attrs.groupType = groupTypeForm } else if b.IsModule() { b.attrs.groupType = groupTypeModule } else { b.attrs.groupType = groupTypeNoModule } utils.AddTextToLog(utils.LogLevel_TRACE, "Окончание инициализации блока") c <- true }
/** * Выполняет обновление конфигурации информационной базы 1С * * @param код результата, который вернуло приложение * * @result код результата и информация об ошибке (если есть) */ func (ib InformationBase) UpdateDBCfg() (resultCode int, err error) { utils.AddTextToLog(utils.LogLevel_INFO, "Обновление ИБ") args := []string{"/UpdateDBCfg"} resultCode, err = ib.runApplication(MODE_DESIGNER, args) utils.AddTextToLog(utils.LogLevel_INFO, fmt.Sprintf("-Обновление ИБ: %t", err == nil)) return }
// Возвращает пары <заголовок атрибутов блока, заголовок данных блока> на основании ЧАСТИ оглавления файла func readPartTOC(data []byte, pathHeader header, pairs *[]headersPair) (err error) { utils.AddTextToLog(utils.LogLevel_TRACE, "Начало чтения части оглавления") dataTOC := getRegion(data, pathHeader) addres := addresInTOC{} addresPath := addres.getElementSize() bytesInElement := addres.getSize() elements := len(dataTOC) / bytesInElement utils.AddTextToLog(utils.LogLevel_TRACE, "Резмер данных оглавления: "+utils.IntToString(len(dataTOC))) utils.AddTextToLog(utils.LogLevel_TRACE, "Количество элементов в оглавлении: "+utils.IntToString(elements)) for i := 0; i < elements; i++ { startPos := i * bytesInElement blockAttrs := dataTOC[startPos : startPos+addresPath] blockData := dataTOC[startPos+addresPath : startPos+addresPath+addresPath] attrsPos := int(utils.BytesToInt32(blockAttrs)) dataPos := int(utils.BytesToInt32(blockData)) if !isHeader(data, attrsPos) || !isHeader(data, dataPos) { continue } attrsHeader, err := createHeader(data, attrsPos) if err != nil { break } dataHeader, err := createHeader(data, dataPos) if err != nil { break } if !attrsHeader.isAttributesData(data) || dataHeader.getDataSize() == 0 { continue } var newPair headersPair newPair.attrs = *attrsHeader newPair.data = *dataHeader *pairs = append(*pairs, newPair) } utils.AddTextToLog(utils.LogLevel_TRACE, "-Окончание чтения части оглавления") return }
/** * Создать новую информационную базу * * @param код результата, который вернуло приложение * @param шаблон конфигурации на основании которого необходимо создать информационную базу (может быть не указан) * * @result код результата и информация об ошибке (если есть) */ func (ib InformationBase) Create(pathToTemplate string) (resultCode int, err error) { utils.AddTextToLog(utils.LogLevel_INFO, "Создание новой ИБ: "+pathToTemplate) if len(pathToTemplate) > 0 { var exist bool exist, err = utils.Exists(pathToTemplate) if err != nil { utils.AddTextToLog(utils.LogLevel_ERROR, err.Error()) return } else if !exist { err = errors.New("Файл не найден: " + pathToTemplate) utils.AddTextToLog(utils.LogLevel_ERROR, err.Error()) return } } err = utils.RemoveIfExist(ib.connectionSettings.GetIbPath()) if err != nil { utils.AddTextToLog(utils.LogLevel_ERROR, "Ошибка удаления каталога: "+ib.connectionSettings.GetIbPath()+":"+err.Error()) return } /* 1С предлагает для создания новой конфигурации на основе существующего cf файла использование параметра /UseTemplate, но по какой-то причине на некоторых версия платформы он работает не корректно. Поэтому делаем в два этапа: 1. создаем новую конфигурацию 2. загружаем в нее нужный cf файл 3. обновляем конфигурацию информационной базы */ args := []string{} resultCode, err = ib.runApplication(MODE_CREATE, args) if err == nil && len(pathToTemplate) > 0 { resultCode, err = ib.LoadCfg(pathToTemplate) } if err == nil { resultCode, err = ib.UpdateDBCfg() } utils.AddTextToLog(utils.LogLevel_INFO, fmt.Sprintf("-Создание новой ИБ: %t", err == nil)) return }
/** * Выгрузка свойств объектов метаданных конфигурации в XML-файлы * * @param код результата, который вернуло приложение * @param каталог, в который будет выгружена конфигурация * * @result код результата и информация об ошибке (если есть) */ func (ib InformationBase) DumpConfigToFiles(pathToDirectory string) (resultCode int, err error) { utils.AddTextToLog(utils.LogLevel_INFO, "Выгрузка конфигурации в xml: "+pathToDirectory) err = utils.RemoveIfExist(pathToDirectory) if err != nil { utils.AddTextToLog(utils.LogLevel_ERROR, "Ошибка удаления каталога: "+pathToDirectory+":"+err.Error()) return } args := []string{"/DumpConfigToFiles", pathToDirectory} resultCode, err = ib.runApplication(MODE_DESIGNER, args) utils.AddTextToLog(utils.LogLevel_INFO, fmt.Sprintf("-Выгрузка конфигурации в xml: %t", err == nil)) return }
/** * Сохранить конфигурацию в файл * * @param код результата, который вернуло приложение * @param путь к файлу результата * * @result код результата и информация об ошибке (если есть) */ func (ib InformationBase) DumpCfg(pathToFile string) (resultCode int, err error) { utils.AddTextToLog(utils.LogLevel_INFO, "Сохранение cf: "+pathToFile) err = utils.RemoveIfExist(pathToFile) if err != nil { utils.AddTextToLog(utils.LogLevel_ERROR, "Ошибка удаления файла: "+pathToFile+":"+err.Error()) return } args := []string{"/DumpCfg", pathToFile} resultCode, err = ib.runApplication(MODE_DESIGNER, args) utils.AddTextToLog(utils.LogLevel_INFO, fmt.Sprintf("-Сохранение cf: %t", err == nil)) return }
// Найти заголовки блоков оглавления (оглавление может состоять из нескольских блоков) // @param данные файла // @result заголовки областей оглавления, описание ошибки (если есть) func findTOC(data []byte) (headerTOC []header, err error) { utils.AddTextToLog(utils.LogLevel_TRACE, "Начало поиска оглавления") err = errors.New("Оглавление не найдено") var previousHeader *header for i := 0; i < len(data); i++ { if !isHeader(data, i) { continue } err = nil utils.AddTextToLog(utils.LogLevel_TRACE, "Позиция части оглавления: "+utils.IntToString(i)) previousHeader, err = createHeader(data, i) break } for true { if err != nil { headerTOC = make([]header, 0) break } headerTOC = append(headerTOC, *previousHeader) if previousHeader.getNextHeaderPosition() == 0 { break } else { i := previousHeader.getNextHeaderPosition() if i > len(data) { break } utils.AddTextToLog(utils.LogLevel_TRACE, "Позиция части оглавления: "+utils.IntToString(i)) previousHeader, err = createHeader(data, i) } } utils.AddTextToLog(utils.LogLevel_TRACE, "-Окончание поиска оглавления") return }
// Инициализировать блок данными из файла // @param данные файла // @param заголовок атрибутов блока // @param заголовок данных блока // @param канал в который будет записана инфорамция что блок инициализирован func (b *block) Init(sourceData []byte, attrsHeader header, dataHeader header, c chan<- bool) { utils.AddTextToLog(utils.LogLevel_TRACE, "Начало инициализации блока") var blockData []byte getAttrsAndData(sourceData, attrsHeader, dataHeader, &b.attrs, &blockData) sourceLen := len(blockData) if sourceLen > 0 { var err error blockData, err = zlibwrapper.Decompress(blockData) if err != nil { panic("Ошибка распаковки блока: " + b.attrs.name) } } headersTOC, err := findTOC(blockData) if err == nil && len(headersTOC) > 0 && bytes.Equal(groupBlocksFlag, blockData[:len(groupBlocksFlag)]) { b.blockType = blockType_multiple subBlocksHeadersPairs, err := readTOC(blockData) if err != nil { panic("Ошибка разбора составного блока: " + err.Error()) } b.subBlocks = make([]subBlock, len(subBlocksHeadersPairs)) for i, p := range subBlocksHeadersPairs { getAttrsAndData(blockData, p.attrs, p.data, &b.subBlocks[i].Attrs, &b.subBlocks[i].Data) } } else { b.data = blockData b.blockType = blockType_simply } utils.AddTextToLog(utils.LogLevel_TRACE, "Окончание инициализации блока") c <- true }
// Создать новый конфигурационный файл на основании данных файлов содержащихся в каталоге func buildCf(pathToDirWithSourceData string, pathToCf string) { pathToTarget := createTargetDir(utils.GetParentDir(pathToCf)) utils.InitLogger(pathToTarget, utils.LogLevel_INFO) utils.AddTextToLog(utils.LogLevel_INFO, "Начало") var err error var objectCf cf.ConfCf if err = objectCf.InitFromCatalog(pathToDirWithSourceData); err != nil { panic(err.Error()) } fileData := objectCf.GetDataForConfigFile() if err = ioutil.WriteFile(pathToCf, fileData, os.ModeAppend); err != nil { panic(err.Error()) } utils.AddTextToLog(utils.LogLevel_INFO, "-Завершение") }
/** * Загрузить конфигурацию из cf файла * * @param полный путь к cf файлу * * @result код результата и информация об ошибке (если есть) */ func (ib InformationBase) LoadCfg(pathToFile string) (resultCode int, err error) { utils.AddTextToLog(utils.LogLevel_INFO, "Загрузка cf:"+pathToFile) exist, err := utils.Exists(pathToFile) if err != nil { utils.AddTextToLog(utils.LogLevel_ERROR, err.Error()) return } else if !exist { err = errors.New("Файл не найден: " + pathToFile) utils.AddTextToLog(utils.LogLevel_ERROR, err.Error()) return } args := []string{"/LoadCfg", pathToFile} resultCode, err = ib.runApplication(MODE_DESIGNER, args) utils.AddTextToLog(utils.LogLevel_INFO, fmt.Sprintf("-Загрузка cf: %t", err == nil)) return }
// Возвращает пары <заголовок атрибутов блока, заголовок данных блока> на основании оглавления файла func readTOC(data []byte) (pairs []headersPair, err error) { utils.AddTextToLog(utils.LogLevel_TRACE, "Начало чтения оглавления") pairs = make([]headersPair, 0) headersTOC, err := findTOC(data) if err == nil { for _, h := range headersTOC { err = readPartTOC(data, h, &pairs) if err != nil { pairs = make([]headersPair, 0) break } } } utils.AddTextToLog(utils.LogLevel_TRACE, "-Окончание чтения оглавления. Найдено блоков: "+utils.IntToString(len(pairs))) return }
/** * Выгрузка свойств объектов метаданных конфигурации (модули и шаблоны) * * @param каталог расположения файлов свойств * @param типы которые выгружаем: * Module — признак необходимости выгрузки модулей; Template — признак необходимости выгрузки макетов; Help — признак необходимости выгрузки справочной информации; AllWritable — признак выгрузки свойств только доступных для записи объектов; Picture — признак выгрузки общих картинок; Right — признак выгрузки прав. * * @result код результата и информация об ошибке (если есть) */ func (ib InformationBase) DumpConfigFiles(pathToDirectory string, types ...string) (resultCode int, err error) { utils.AddTextToLog(utils.LogLevel_INFO, "Выгрузка конфигурации: "+pathToDirectory) err = utils.RemoveIfExist(pathToDirectory) if err != nil { utils.AddTextToLog(utils.LogLevel_ERROR, "Ошибка удаления каталога: "+pathToDirectory+":"+err.Error()) return } // /DumpConfigFiles "{0}" -Module -Template args := []string{"/DumpConfigFiles", pathToDirectory} for _, v := range types { args = append(args, "-"+v) } resultCode, err = ib.runApplication(MODE_DESIGNER, args) utils.AddTextToLog(utils.LogLevel_INFO, fmt.Sprintf("-Выгрузка конфигурации: %t", err == nil)) return }
/** * Загрузка свойств объектов метаданных конфигурации * * @param код результата, который вернуло приложение * @param каталог, содержащий XML-файлы конфигурации * * @result код результата и информация об ошибке (если есть) */ func (ib InformationBase) LoadConfigFromFiles(pathToDirectory string) (resultCode int, err error) { utils.AddTextToLog(utils.LogLevel_INFO, "Загрузка конфигурации из xml: "+pathToDirectory) exist, err := utils.Exists(pathToDirectory) if err != nil { utils.AddTextToLog(utils.LogLevel_ERROR, err.Error()) return } else if !exist { err = errors.New("Каталог не найден: " + pathToDirectory) utils.AddTextToLog(utils.LogLevel_ERROR, err.Error()) return } args := []string{"/LoadConfigFromFiles", pathToDirectory} resultCode, err = ib.runApplication(MODE_DESIGNER, args) utils.AddTextToLog(utils.LogLevel_INFO, fmt.Sprintf("-Загрузка конфигурации из xml: %t", err == nil)) return }
// Добавляет оглавление группового блока func addTableOfContent(fileData *[]byte, addresses []addresInTOC) { utils.AddTextToLog(utils.LogLevel_TRACE, "Начало добавления оглавления") addres := addresInTOC{} delimetr := utils.Int32ToBytes(emptyValue) countBlocks := len(addresses) valuableTOCSize := addres.getSize() * countBlocks fullTOCSize := valuableTOCSize if fullTOCSize < defaultBlockSize { fullTOCSize = defaultBlockSize } data := []byte{} utils.AddToSlice(&data, groupBlocksFlag, // маркер группы utils.Int32ToBytes(defaultBlockSize), // размер блока по умолчанию []byte{0x00, 0x00, 0x00, 0x00}, // unknown []byte{0x00, 0x00, 0x00, 0x00}, // unknown getHeaderForCf(valuableTOCSize, fullTOCSize)) // Заголовок области оглавления beginFileDataLen := int32(len(data) + fullTOCSize) for _, v := range addresses { utils.AddToSlice(&data, utils.Int32ToBytes(v.AttrsPos+beginFileDataLen), // позиция заголовка атрибутов блока utils.Int32ToBytes(v.DataPos+beginFileDataLen), // позиция заголовка данных блока delimetr) // макер разделителя адресов } emptyData := []byte{} if valuableTOCSize < fullTOCSize { emptyData = make([]byte, fullTOCSize-valuableTOCSize) } data = append(data, emptyData...) *fileData = append(data, (*fileData)...) utils.AddTextToLog(utils.LogLevel_TRACE, "-Окончание добавления оглавления") }
// Инициализация объекта данными из каталого содержащего распакованный конфигурационный блок func (c ConfCf) InitFromCatalog(pathToDir string) (err error) { utils.AddTextToLog(utils.LogLevel_INFO, "Начало инициализации объекта данными конфигурационного файла из каталога") var fileInfos []os.FileInfo if fileInfos, err = utils.ReadFilesInDir(pathToDir); err != nil { return err } funcForInitBlocks := func(numStream int, processBlocksTotal int, countBlocks int, blockInitResultChanal chan bool) { for i := processBlocksTotal; i < countBlocks && i-processBlocksTotal != numStream; i++ { fi := fileInfos[i] go c.blocksList[i].InitFromFiles(pathToDir, fi, blockInitResultChanal) } } c.initBlocks(len(fileInfos), funcForInitBlocks) utils.AddTextToLog(utils.LogLevel_INFO, "-Окончание инициализации объекта данными конфигурационного файла из каталога") return nil }
// Распаковать конфигурационный файл в каталог func unpackToDir(pathToCf string, pathToTarget string) { createTargetDir(pathToTarget) utils.InitLogger(pathToTarget, utils.LogLevel_INFO) utils.AddTextToLog(utils.LogLevel_INFO, "Начало") var err error var fileData []byte if fileData, err = ioutil.ReadFile(pathToCf); err != nil { panic(err.Error()) } pathToUnpackDir := createTargetDir(path.Join(pathToTarget, "unpack")) var objectCf cf.ConfCf if err = objectCf.InitFromFile(fileData); err != nil { panic(err.Error()) } objectCf.SaveBlocksToFiles(pathToUnpackDir) utils.AddTextToLog(utils.LogLevel_INFO, "-Завершение") }